#include "wxsheaders.h"
#include "wxscoder.h"

#include <manager.h>
#include <editormanager.h>
#include <messagemanager.h>
#include <wx/ffile.h>

#include "wxsglobals.h"

#define DebLog Manager::Get()->GetMessageManager()->DebugLog

wxsCoder::wxsCoder():
	Enteries(NULL),
	BlockProcess(false)
{
}

wxsCoder::~wxsCoder()
{
	ProcessCodeQueue();
}

void wxsCoder::AddCode(const wxString& FileName,const wxString& BlockHeader,const wxString& Code,bool Immediately)
{
	DataMutex.Lock();

	// Iterating through all enteries searching for currently associated code

	CodeEntry* Entry;
	CodeEntry* Previous;
	for ( Entry = Enteries, Previous = NULL;
	      Entry != NULL;
	      Previous = Entry, Entry = Entry->Next )
	{
		if ( Entry->FileName == FileName && Entry->BlockHeader == BlockHeader )
			break;
	}

	if ( Entry )
	{
		( Previous ? Previous->Next : Enteries ) = Entry->Next;
		Previous = Entry;
		if ( Previous ) while ( Previous->Next ) Previous = Previous->Next;
	}
	else
	{
		Entry = new CodeEntry;
		Entry->FileName = FileName;
		Entry->BlockHeader = BlockHeader;
	}

	Entry->Code = Code;

	if ( Immediately )
	{
		ApplyChanges(Entry);
		delete Entry;
	}
	else
	{
        time(&Entry->TimeStamp);
        Entry->Next = NULL;
        ( Previous ? Previous->Next : Enteries ) = Entry;
	}
	DataMutex.Unlock();

// TODO (SpOoN#1#): Add sheduling and replace direct processing

	ProcessCodeQueue();
}

bool wxsCoder::ProcessCodeQueue()
{
	wxMutexLocker Locker(DataMutex);

	if ( BlockProcess ) return false;
	BlockProcess = true;

	bool Result = true;
	time_t now;
	time(&now);

// TODO (SpOoN#1#): Process requests which are staying inside queue long enough
	while ( Enteries )
	{
		CodeEntry* Entry = Enteries;
		ApplyChanges(Entry);
		Enteries = Enteries->Next;
		delete Entry;
	}

	BlockProcess = false;
	return Result;
}

void wxsCoder::DropQueue()
{
	wxMutexLocker Locker(DataMutex);

	for ( CodeEntry* Entry = Enteries, *Next; Entry; Entry = Next )
	{
		Next = Entry->Next;
		delete Entry;
	}
}

bool wxsCoder::ApplyChanges(wxsCoder::CodeEntry* Entry)
{
	EditorManager* EM = Manager::Get()->GetEditorManager();
	assert ( EM != NULL );

    cbEditor* Editor = EM->GetBuiltinEditor(Entry->FileName);
    if ( Editor )
    {
        return ApplyChanges(Entry,Editor);
    }

    return ApplyChanges(Entry,Entry->FileName);
}

bool wxsCoder::ApplyChanges(wxsCoder::CodeEntry* Entry,cbEditor* Editor)
{
	assert ( Editor != NULL );
	cbStyledTextCtrl* Ctrl = Editor->GetControl();
	assert ( Ctrl != NULL );

	Ctrl->SetSearchFlags(wxSCI_FIND_MATCHCASE);

	Ctrl->SetTargetStart(0);
	Ctrl->SetTargetEnd(Ctrl->GetLength());

	int Position = Ctrl->SearchInTarget(Entry->BlockHeader);

	if ( Position == -1 )
	{
		wxMessageBox(wxString::Format(
			_("Couldn't find code with header:\n\t\"%s\"\nin file '%s'"),
			Entry->BlockHeader.c_str(),
			Editor->GetFilename().c_str()));
		return false;
	}

    // Beginning of this code block is in Position, now searching for end
    Ctrl->SetTargetStart(Position);
    Ctrl->SetTargetEnd(Ctrl->GetLength());
    int End = Ctrl->SearchInTarget(wxsBEnd());
    if ( End == -1 )
    {
        wxMessageBox(wxString::Format(
            _("Unfinished block of auto-generated code with header:\n\t\"%s\"\nin file '%s'"),
            Entry->BlockHeader.c_str(),
            Editor->GetFilename().c_str()));
        return false;
    }
    else
    {
        Ctrl->SetTargetStart(Position);
        Ctrl->SetTargetEnd(End);
        Ctrl->ReplaceTarget(Entry->Code);
        Editor->SetModified();
    }

	return true;
}

bool wxsCoder::ApplyChanges(wxsCoder::CodeEntry* Entry,const wxString& FileName)
{
    wxFFile File(FileName,_T("r"));
    if ( !File.IsOpened() )
    {
    	/*
		wxMessageBox(wxString::Format(
			_("Couldn't open file '%s' for reading"),
			FileName.c_str()));
        */
    	return false;
    }

    wxString Content;
    if ( !File.ReadAll(&Content) )
    {
    	/*
		wxMessageBox(wxString::Format(
			_("Couldn't read from file '%s'"),
			FileName.c_str()));
        */
    	return false;
    }

    int Position = Content.First(Entry->BlockHeader);

    if ( Position == -1 )
    {
    	/*
		wxMessageBox(wxString::Format(
			_("Couldn't find code with header:\n\t\"%s\"\nin file '%s'"),
			Entry->BlockHeader.c_str(),
			FileName.c_str()));
        */
		return false;
    }

    wxString Result = Content.Left(Position);
    Content.Remove(0,Position);
    Position = Content.First(wxsBEnd());
    if ( Position == -1 )
    {
    	/*
        wxMessageBox(wxString::Format(
            _("Unfinished block of auto-generated code with header:\n\t\"%s\"\nin file '%s'"),
            Entry->BlockHeader.c_str(),
            FileName.c_str()));
        */
        return false;
    }

// FIXME (SpOoN#1#): Rebuild new code to support valid eol mode
    Result += Entry->Code;
    Result += Content.Remove(0,Position);

    File.Close();

    if ( !File.Open(FileName,_T("w")) )
    {
    	/*
		wxMessageBox(wxString::Format(
			_("Couldn't open file '%s' for writing"),
			FileName.c_str()));
        */
    	return false;
    }

    if ( !File.Write(Result) )
    {
    	/*
		wxMessageBox(wxString::Format(
			_("Couldn't write to file '%s'"),
			FileName.c_str()));
        */
    	return false;
    }

	return true;
}

bool wxsCoder::ProcessCodeForFile(const wxString& FileName)
{
	wxMutexLocker Locker(DataMutex);

	bool Result = true;

	for ( CodeEntry* Entry = Enteries, *Previous = NULL; Entry; )
	{
		if ( Entry->FileName == FileName )
		{
			ApplyChanges(Entry);
			CodeEntry* Next = ( Previous ? Previous->Next : Enteries ) = Entry->Next;
			delete Entry;
			Entry = Next;
		}
		else
		{
			Previous = Entry;
			Entry = Entry->Next;
		}
	}

	return Result;
}

static wxsCoder SingletonObject;
wxsCoder* wxsCoder::Singleton = &SingletonObject;

wxString wxsCoder::GetCode(const wxString& FileName,const wxString& BlockHeader)
{
	EditorManager* EM = Manager::Get()->GetEditorManager();
	assert ( EM != NULL );
    cbEditor* Editor = EM->GetBuiltinEditor(FileName);
    if ( Editor )
    {
        cbStyledTextCtrl* Ctrl = Editor->GetControl();
        Ctrl->SetSearchFlags(wxSCI_FIND_MATCHCASE);
        Ctrl->SetTargetStart(0);
        Ctrl->SetTargetEnd(Ctrl->GetLength());
        int Position = Ctrl->SearchInTarget(BlockHeader);
        if ( Position == -1 ) return _T("");
        Ctrl->SetTargetStart(Position);
        Ctrl->SetTargetEnd(Ctrl->GetLength());
        int End = Ctrl->SearchInTarget(wxsBEnd());
        if ( End == -1 ) return _T("");
        return Ctrl->GetTextRange(Position,End);
    }

    wxFFile File(FileName,_T("r"));
    wxString Content;
    if ( !File.IsOpened() ) return _T("");
    if ( !File.ReadAll(&Content) ) return _T("");
    int Position = Content.First(BlockHeader);
    if ( Position == -1 ) return _T("");
    Content.Remove(0,Position);
    Position = Content.First(wxsBEnd());
    if ( Position == -1 ) return _T("");
    Content.Remove(Position);
	return Content;
}


syntax highlighted by Code2HTML, v. 0.9.1