#include "ide.h"

String GetDefaultMethod()
{
	return LoadFile(ConfigFile("default_method"));
}

VectorMap<String, String> GetMethodVars(const String& method)
{
	VectorMap<String, String> map;
	LoadVarFile(ConfigFile((String)~method + ".bm"), map);
	return map;
}

class TextOption : public Option {
public:
	virtual void   SetData(const Value& data);
	virtual Value  GetData() const;
};

void  TextOption::SetData(const Value& data)
{
	String s = data;
	Set(!(IsNull(s) || s == "0"));
}

Value TextOption::GetData() const
{
	return Get() ? "1" : "0";
}

class TextSwitch : public Switch {
public:
	virtual void   SetData(const Value& data);
	virtual Value  GetData() const;
};

void  TextSwitch::SetData(const Value& data)
{
	String s = data;
	Switch::SetData(atoi((String)data));
}

Value TextSwitch::GetData() const
{
	return AsString(Switch::GetData());
}

class DirTable : public ArrayCtrl {
public:
	virtual void   SetData(const Value& data);
	virtual Value  GetData() const;

protected:
	void Modify()  { Update(); }

	EditString      edit;
	SelectDirButton edit_dir;

public:
	DirTable(const char *name);
};

void   DirTable::SetData(const Value& data)
{
	Vector<String> l = Split((String)data, ';');
	Clear();
	for(int i = 0; i < l.GetCount(); i++)
		Add(l[i]);
}

Value  DirTable::GetData() const
{
	String s;
	for(int i = 0; i < GetCount(); i++) {
		if(i) s << ';';
		s << (String)Get(i, 0);
	}
	return s;
}

DirTable::DirTable(const char *name)
{
	AutoHideSb();
	AddColumn(name).Edit(edit);
	Appending().Removing().Moving();
	edit_dir.Attach(edit);
	edit_dir.AllFilesType();
	WhenArrayAction = edit <<= callback(this, &DirTable::Modify);
}

class DirMap : public ArrayCtrl {
public:
	virtual void   SetData(const Value& data);
	virtual Value  GetData() const;

protected:
	void Modify()  { Update(); }

	EditString      localpath, remotepath;
	SelectDirButton edit_dir;

public:
	DirMap();
};

void DirMap::SetData(const Value& data)
{
	Vector<String> l = Split((String)data, ';');
	Clear();
	for(int i = 0; i < l.GetCount(); i++) {
		String li = l[i];
		int f = li.Find('>');
		if(f >= 0)
			Add(li.Left(f), li.Mid(f + 1));
	}
}

Value DirMap::GetData() const
{
	String s;
	for(int i = 0; i < GetCount(); i++) {
		if(i) s << ';';
		s << (String)Get(i, 0) << '>' << (String)Get(i, 1);
	}
	return s;
}

DirMap::DirMap()
{
	AutoHideSb();
	AddColumn("Map local path").Edit(localpath);
	AddColumn("To remote path").Edit(remotepath);
	Appending().Removing().Moving();
	edit_dir.Attach(localpath);
	WhenArrayAction = localpath <<= remotepath <<= callback(this, &DirMap::Modify);
}

struct BuildMethods : public WithBuildMethodsLayout<TopWindow>
{
	TextOption debug_blitz;
	TextSwitch debug_linkmode;
	TextOption release_blitz;
	TextSwitch release_linkmode;
	DirTable   path;
	DirTable   include;
	DirTable   lib;
	DirMap     remote_path_map;
	OpenFileButton open_script;

	EditStringNotNull name;
	Index<String>     origfile;
	String            default_method;

	void Load();
	bool Save();

	void NewBuilder();
	void ShowDefault();
	void SetDefault();
	void ChangeMethod();
	void Import();

	void MethodMenu(Bar& bar);

	typedef BuildMethods CLASSNAME;

	BuildMethods();
};

int CharFilterFileName(int c)
{
	return IsAlNum(c) || c == '_' ? c : 0;
}

BuildMethods::BuildMethods()
:	path("PATH - executable directories"),
	include("INCLUDE - directories for include files"),
	lib("LIB - directories for library files")
{
	CtrlLayoutOKCancel(*this, "Build methods");
	method.AddColumn("Method").Edit(name);
	name.SetFilter(CharFilterFileName);
	method.AddCtrl("BUILDER", builder);
	method.AddCtrl("COMPILER", compiler);
	method.AddCtrl("DEBUG_INFO", debug_info);
	method.AddCtrl("DEBUG_BLITZ", debug_blitz);
	method.AddCtrl("DEBUG_LINKMODE", debug_linkmode);
	method.AddCtrl("DEBUG_OPTIONS", debug_options);
	method.AddCtrl("RELEASE_BLITZ", release_blitz);
	method.AddCtrl("RELEASE_LINKMODE", release_linkmode);
	method.AddCtrl("RELEASE_OPTIONS", speed_options);
	method.AddCtrl("RELEASE_SIZE_OPTIONS", size_options);
	method.AddCtrl("DEBUGGER", debugger);
	method.AddCtrl("PATH", path);
	method.AddCtrl("INCLUDE", include);
	method.AddCtrl("LIB", lib);
	method.AddCtrl("REMOTE_HOST", remote_host);
	method.AddCtrl("REMOTE_OS", remote_os);
	remote_os.Add("WIN32");
	remote_os.Add("LINUX");
	remote_os.Add("WINCE");
	remote_os.Add("UNIX");
	remote_os.Add("SOLARIS");
	remote_os.Add("FREEBSD");
	method.AddCtrl("REMOTE_TRANSFER", remote_file_access);
	remote_file_access.Add("0", "direct (SAMBA)");
	remote_file_access.Add("1", "indirect (transfer)");
	method.AddCtrl("REMOTE_MAP", remote_path_map);
	method.AddCtrl("SCRIPT", scriptfile);
	open_script.Attach(scriptfile);
	open_script.Type("Build scripts (*.bsc)", "*.bsc")
		.AllFilesType();
	open_script.DefaultExt("bsc");
	method.Appending().Removing().Duplicating();
	method.WhenCursor = THISBACK(ChangeMethod);
	method.WhenBar = THISBACK(MethodMenu);

	debug_info.Add("0", "None");
	debug_info.Add("1", "Minimal");
	debug_info.Add("2", "Full");

	for(int i = 0; i < BuilderMap().GetCount(); i++)
		builder.Add(BuilderMap().GetKey(i));

	builder <<= THISBACK(NewBuilder);
	setdefault <<= THISBACK(SetDefault);
}

void BuildMethods::MethodMenu(Bar& bar)
{
	method.StdBar(bar);
	bar.Separator();
	bar.Add("Import", THISBACK(Import));
}

void BuildMethods::Import()
{
	if(!Save())
		return;
	FileSel fsel;
	fsel.Type("Build methods (*.bm)", "*.bm")
		.AllFilesType()
		.Multi()
		.DefaultExt("bm");
	if(!fsel.ExecuteOpen())
		return;
	for(int i = 0; i < fsel.GetCount(); i++) {
		String f = LoadFile(fsel[i]);
		if(f.IsVoid()) {
			if(!PromptOKCancel(NFormat("Failed to load [* \1%s\1]. Continue?", fsel[i])))
				break;
			continue;
		}
		String nf = ConfigFile(GetFileNamePos(fsel[i]));
		if(FileExists(nf) && !PromptOKCancel(NFormat("File already exists: [* \1%s\1]. Overwrite?", nf)))
			continue;
		if(!SaveFile(nf, f))
			if(!PromptOKCancel(NFormat("Failed to save [* \1%s\1]. Continue?", nf)))
				break;
	}
	Load();
}

static int sCompare(const Value& v1, const Value& v2)
{
	return (String)v1 < (String)v2;
}

static bool IsGccBuilder(String b) { return b == "GCC" || b == "GCC32" || b == "GCC_ARM"; }

void BuildMethods::NewBuilder()
{
	String b = ~builder;
	if(IsNull(speed_options)) {
		if(IsGccBuilder(b))
		#ifdef PLATFORM_WIN32
			speed_options <<= "-O3 -ffunction-sections";
		#else
			speed_options <<= "-O3";
		#endif
		else
			speed_options <<= "-O2";
	}
	if(IsNull(size_options)) {
		if(IsGccBuilder(b))
		#ifdef PLATFORM_WIN32
			size_options <<= "-Os -finline-limit=20 -ffunction-sections";
		#else
			size_options <<= "-Os -finline-limit=20";
		#endif
		else
			size_options <<= "-O1";
	}
	if(IsNull(debug_options)) {
		if(IsGccBuilder(b))
			debug_options <<= "-O0";
		else
			debug_options <<= "-Od";
	}
	if(IsNull(debugger)) {
		if(IsGccBuilder(b))
			debugger <<= "gdb";
		else
			debugger <<= "msdev";
	}
	ChangeMethod();
}

void BuildMethods::ChangeMethod()
{
	String b;
	if(method.IsCursor())
		b = method.Get("BUILDER");
	scriptfile.Enable(b == "SCRIPT");
}

void BuildMethods::Load()
{
	FindFile ff(ConfigFile("*.bm"));
	while(ff) {
		VectorMap<String, String> map;
		String fn = ConfigFile(ff.GetName());
		if(LoadVarFile(fn, map)) {
			origfile.Add(fn);
			method.Add(GetFileTitle(fn));
			for(int j = 1; j < method.GetIndexCount(); j++)
				method.Set(method.GetCount() - 1, j, map.Get(method.GetId(j).ToString(), Null));
		}
		ff.Next();
	}
	method.Sort(0, sCompare);
	method.GoBegin();
	NewBuilder();
}

bool BuildMethods::Save()
{
	int i;
	Index<String> name;
	for(i = 0; i < method.GetCount(); i++) {
		String n = method.Get(i, 0);
		if(name.Find(n) >= 0) {
			Exclamation("Duplicate method [* " + DeQtf(n) + "] !");
			return false;
		}
		name.Add(n);
	}
	Index<String> saved;
	for(i = 0; i < method.GetCount(); i++) {
		VectorMap<String, String> map;
		for(int j = 1; j < method.GetIndexCount(); j++)
			map.Add(method.GetId(j).ToString(), method.Get(i, j));
		if(map.Get("BUILDER", "") != "SCRIPT")
			map.RemoveKey("SCRIPT");
		String fn = ConfigFile(String(method.Get(i, 0)) + ".bm");
		if(!SaveVarFile(fn, map)) {
			Exclamation("Error saving [* " + fn + "] !");
			return false;
		}
		saved.Add(fn);
	}
	for(i = 0; i < origfile.GetCount(); i++)
		if(saved.Find(origfile[i]) < 0)
			DeleteFile(origfile[i]);
	return true;
}

struct BoldDisplay : Display {
	virtual void Paint(Draw& w, const Rect& r, const Value& q,
					   Color ink, Color paper, dword style) const {
		w.DrawRect(r, paper);
		DrawSmartText(w, r.left, r.top, r.Width(), (String)q, StdFont().Bold(), ink);
	}
};


void BuildMethods::ShowDefault()
{
	String m = GetDefaultMethod();
	for(int i = 0; i < method.GetCount(); i++)
		if((String)method.Get(i, 0) == m)
			method.SetDisplay(i, 0, Single<BoldDisplay>());
		else
			method.SetDisplay(i, 0, StdDisplay());
}

void BuildMethods::SetDefault()
{
	if(method.IsCursor()) {
		SaveFile(ConfigFile("default_method"), method.GetKey());
		ShowDefault();
	}
}

void Ide::SetupBuildMethods()
{
	BuildMethods m;
	m.Load();
	m.ShowDefault();
	m.use_target = use_target;
	for(;;) {
		int c = m.Run();
		if(c == IDCANCEL)
			break;
		if(c == IDOK && m.Save()) {
			use_target = m.use_target;
			break;
		}
	}
	SyncBuildMode();
	SetBar();
}
