/*
    cbp2make : Makefile generation tool for the Code::Blocks IDE
    Copyright (C) 2010-2013 Mirai Computing (mirai.computing@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//------------------------------------------------------------------------------
#include <iostream>
#include "cbhelper.h"

#include "stlfutils.h"
#include "depsearch.h"
//------------------------------------------------------------------------------

CDependencyRecord::CDependencyRecord(void)
{
    Clear();
}

CDependencyRecord::~CDependencyRecord(void)
{
    Clear();
}

void CDependencyRecord::SetName(const CString& Name)
{
    m_Name = Name;
    m_Hash = sdbm_hash((unsigned char *)Name.GetCString(),Name.GetLength());
}

void CDependencyRecord::Clear(void)
{
    m_Hash = 0;
    m_Name.Clear();
    m_Dependencies.clear();
    m_CrossReferences.clear();
    m_DependenciesComplete = false;
    m_CrossReferencesComplete = false;
    m_Marker = 0;
}

void CDependencyRecord::Show(void)
{
    std::cout<<"Unit: "<<m_Name.GetCString()<<std::endl;
    if (m_Dependencies.size() > 0) {
        std::cout<<"Unit dependens on "<<m_Dependencies.size()<<" other units."<<std::endl;
        for (size_t i = 0; i < m_Dependencies.size(); i++) {
            CDependencyRecord *r = m_Dependencies[i];
            std::cout<<"Dependency #"<<(i+1)<<": "<<r->GetName().GetCString()<<std::endl;
        }
    } else {
        std::cout<<"Unit has no dependencies."<<std::endl;
    }
    if (m_CrossReferences.size() > 0) {
        std::cout<<"Unit is used by"<<m_CrossReferences.size()<<" other units."<<std::endl;
        for (size_t i = 0; i < m_CrossReferences.size(); i++) {
            CDependencyRecord *r = m_CrossReferences[i];
            std::cout<<"Cross reference #"<<(i+1)<<": "<<r->GetName().GetCString()<<std::endl;
        }
    } else {
        std::cout<<"Unit is not used by any other unit."<<std::endl;
    }
}

size_t CDependencyRecord::DependenciesCount(void) const
{
    return m_Dependencies.size();
}

CDependencyRecord *CDependencyRecord::Dependency(const size_t Index) const
{
    if (Index<m_Dependencies.size()) {
        return m_Dependencies[Index];
    }
    return 0;
}

void CDependencyRecord::AddDependency(const CDependencyRecord *Record)
{
    for (size_t i = 0; i < m_Dependencies.size(); i++) {
        if (Record == m_Dependencies[i]) return;
    }
    m_Dependencies.push_back((CDependencyRecord *)Record);
}

size_t CDependencyRecord::CrossReferencesCount(void) const
{
    return m_CrossReferences.size();
}

CDependencyRecord *CDependencyRecord::CrossReference(const size_t Index) const
{
    if (Index<m_CrossReferences.size()) {
        return m_CrossReferences[Index];
    }
    return 0;
}

void CDependencyRecord::AddCrossReference(const CDependencyRecord *Record)
{
    for (size_t i = 0; i < m_CrossReferences.size(); i++) {
        if (Record == m_CrossReferences[i]) return;
    }
    m_CrossReferences.push_back((CDependencyRecord *)Record);
}

//------------------------------------------------------------------------------

CDependencyInfo::CDependencyInfo(void)
{
    Clear();
}

CDependencyInfo::~CDependencyInfo(void)
{
    Clear();
}

void CDependencyInfo::Clear(void)
{
    for (size_t i = 0; i < m_Records.size(); i++) {
        delete m_Records[i];
    }
    m_Records.clear();
    m_Platform.Clear();
}

void CDependencyInfo::Show(void)
{
    if (m_Records.size() > 0) {
        std::cout<<"Dependency info collected for "<<m_Records.size()<<" units."<<std::endl;
        std::cout<<"Unit Dependencies: Direct\tIndirect\tAll\t"
                 <<"XRefs: Direct\tIndirect\tAll\tPath"<<std::endl;
        for (size_t i = 0; i < m_Records.size(); i++) {
            CDependencyRecord *r = m_Records[i];
            /*
            std::cout<<"Unit #"<<(i+1)<<" '"<<r->GetName().GetCString()<<"' has "
            <<r->DependenciesCount()<<" direct and "<<DependenciesCount(r)
            <<" indirect dependencies."<<std::endl;
            */
            std::cout<<"#"<<(i+1)<<"\t\t"<<r->DependenciesCount();
            if (r->DependenciesComplete()) std::cout<<"*";
            std::cout<<"\t\t"<<IndirectDependenciesCount(i)<<"\t\t"
                     <<AllDependenciesCount(i)<<"\t"<<r->CrossReferencesCount();
            if (r->CrossReferencesComplete()) std::cout<<"*";
            std::cout<<"\t\t"<<IndirectCrossReferencesCount(i)<<"\t\t"
                     <<AllCrossReferencesCount(i)<<"\t"<<r->GetName().GetCString()<<std::endl;
        }
    } else {
        std::cout<<"Dependency information is not available."<<std::endl;
    }
}

CString CDependencyInfo::OneLineReport(const size_t Index, const bool Deps, const bool XRefs)
{
    CString result;
    if (Index<m_Records.size()) {
        CDependencyRecord *r = m_Records[Index];
        if (Deps) {
            result += "Deps ["+IntegerToString(r->DependenciesCount())
                      + "/"+IntegerToString(IndirectDependenciesCount(Index))
                      + "/"+IntegerToString(AllDependenciesCount(Index)) + "]";
        }
        if (Deps && XRefs) result += " ";
        if (XRefs) {
            result += " XRefs ["+IntegerToString(r->CrossReferencesCount())
                      + "/"+IntegerToString(IndirectCrossReferencesCount(Index))
                      + "/"+IntegerToString(AllCrossReferencesCount(Index))+"]";
        }
        result += " (direct/indirect/all)";
    }
    return result;
}

void CDependencyInfo::ResetMarkers(void)
{
    for (size_t i = 0; i < m_Records.size(); i++) {
        m_Records[i]->Marker() = 0;
    }
}

size_t CDependencyInfo::DependenciesCount(CDependencyRecord *Record)
{
    size_t result = 0;
    if (0!=Record) {
        //ResetMarkers();
        result += Record->DependenciesCount();
        Record->Marker()++;
        for (size_t i = 0, n = Record->DependenciesCount(); i < n; i++) {
            if (0==Record->Dependency(i)->Marker()) {
                result += DependenciesCount(Record->Dependency(i));
                Record->Dependency(i)->Marker()++;
            }
        }
    }
    return result;
}

CStringList CDependencyInfo::Dependencies(CDependencyRecord *Record)
{
    CStringList result;
    if (0!=Record) {
        //ResetMarkers();
        result.Insert(Record->GetName());
        Record->Marker()++;
        for (size_t i = 0, n = Record->DependenciesCount(); i < n; i++) {
            if (0==Record->Dependency(i)->Marker()) {
                result.Insert(Dependencies(Record->Dependency(i)));
                Record->Dependency(i)->Marker()++;
            }
        }
    }
    return result;
}

size_t CDependencyInfo::CrossReferencesCount(CDependencyRecord *Record)
{
    size_t result = 0;
    if (0!=Record) {
        //ResetMarkers();
        result += Record->CrossReferencesCount();
        Record->Marker()++;
        for (size_t i = 0, n = Record->CrossReferencesCount(); i < n; i++) {
            if (0==Record->CrossReference(i)->Marker()) {
                result += CrossReferencesCount(Record->CrossReference(i));
                Record->CrossReference(i)->Marker()++;
            }
        }
    }
    return result;
}

CStringList CDependencyInfo::CrossReferences(CDependencyRecord *Record)
{
    CStringList result;
    if (0!=Record) {
        //ResetMarkers();
        result.Insert(Record->GetName());
        Record->Marker()++;
        for (size_t i = 0, n = Record->CrossReferencesCount(); i < n; i++) {
            if (0==Record->CrossReference(i)->Marker()) {
                result.Insert(CrossReferences(Record->CrossReference(i)));
                Record->CrossReference(i)->Marker()++;
            }
        }
    }
    return result;
}

size_t CDependencyInfo::RecordsCount(void) const
{
    return m_Records.size();
}

size_t CDependencyInfo::DirectDependenciesCount(const size_t Index) const
{
    size_t result = 0;
    if (Index<m_Records.size()) {
        result += m_Records[Index]->DependenciesCount();
    }
    return result;
}

size_t CDependencyInfo::IndirectDependenciesCount(const size_t Index)
{
    size_t result = 0;
    if (Index<m_Records.size()) {
        ResetMarkers();
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->DependenciesCount(); i < n; i++) {
            result += DependenciesCount(r->Dependency(i));
        }
    }
    return result;
}

size_t CDependencyInfo::AllDependenciesCount(const size_t Index)
{
    size_t result = 0;
    if (Index<m_Records.size()) {
        ResetMarkers();
        return DependenciesCount(m_Records[Index]);
    }
    return result;
}

CString CDependencyInfo::Name(const size_t Index) const
{
    if (Index<m_Records.size()) {
        return m_Records[Index]->GetName();
    }
    return "";
}

CStringList CDependencyInfo::DirectDependencies(const size_t Index) const
{
    CStringList result;
    if (Index<m_Records.size()) {
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->DependenciesCount(); i < n; i++) {
            result.Insert(r->Dependency(i)->GetName());
        }
    }
    return result;
}

CStringList CDependencyInfo::IndirectDependencies(const size_t Index)
{
    CStringList result;
    if (Index<m_Records.size()) {
        ResetMarkers();
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->DependenciesCount(); i < n; i++) {
            const CDependencyRecord *rr = r->Dependency(i);
            for (size_t j = 0, m = rr->DependenciesCount(); j < m; j++) {
                result.Insert(Dependencies(rr->Dependency(j)));
            }
        }
    }
    return result;
}

CStringList CDependencyInfo::AllDependencies(const size_t Index)
{
    CStringList result;
    if (Index<m_Records.size()) {
        ResetMarkers();
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->DependenciesCount(); i < n; i++) {
            result.Insert(Dependencies(r->Dependency(i)));
        }
    }
    return result;
}

size_t CDependencyInfo::DirectCrossReferencesCount(const size_t Index) const
{
    size_t result = 0;
    if (Index<m_Records.size()) {
        result += m_Records[Index]->CrossReferencesCount();
    }
    return result;
}

size_t CDependencyInfo::IndirectCrossReferencesCount(const size_t Index)
{
    size_t result = 0;
    if (Index<m_Records.size()) {
        ResetMarkers();
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->CrossReferencesCount(); i < n; i++) {
            result += CrossReferencesCount(r->CrossReference(i));
        }
    }
    return result;
}

size_t CDependencyInfo::AllCrossReferencesCount(const size_t Index)
{
    size_t result = 0;
    if (Index<m_Records.size()) {
        ResetMarkers();
        return CrossReferencesCount(m_Records[Index]);
    }
    return result;
}

CStringList CDependencyInfo::DirectCrossReferences(const size_t Index) const
{
    CStringList result;
    if (Index<m_Records.size()) {
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->CrossReferencesCount(); i < n; i++) {
            result.Insert(r->CrossReference(i)->GetName());
        }
    }
    return result;
}

CStringList CDependencyInfo::IndirectCrossReferences(const size_t Index)
{
    CStringList result;
    if (Index<m_Records.size()) {
        ResetMarkers();
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->CrossReferencesCount(); i < n; i++) {
            const CDependencyRecord *rr = r->CrossReference(i);
            for (size_t j = 0, m = rr->CrossReferencesCount(); j < m; j++) {
                result.Insert(CrossReferences(rr->CrossReference(j)));
            }
        }
    }
    return result;
}

CStringList CDependencyInfo::AllCrossReferences(const size_t Index)
{
    CStringList result;
    if (Index<m_Records.size()) {
        ResetMarkers();
        const CDependencyRecord *r = m_Records[Index];
        for (size_t i = 0, n = r->CrossReferencesCount(); i < n; i++) {
            result.Insert(CrossReferences(r->CrossReference(i)));
        }
    }
    return result;
}

bool CDependencyInfo::AreDependenciesComplete(const size_t Index)
{
    if (Index<m_Records.size()) {
        return m_Records[Index]->DependenciesComplete();
    }
    return false;
}

bool CDependencyInfo::AreCrossReferencesComplete(const size_t Index)
{
    if (Index<m_Records.size()) {
        return m_Records[Index]->CrossReferencesComplete();
    }
    return false;
}

void CDependencyInfo::SetDependenciesComplete(const size_t Index, const bool State)
{
    if (Index<m_Records.size()) {
        m_Records[Index]->DependenciesComplete() = State;
    }
}

void CDependencyInfo::SetCrossReferencesComplete(const size_t Index, const bool State)
{
    if (Index<m_Records.size()) {
        m_Records[Index]->CrossReferencesComplete() = State;
    }
}

int CDependencyInfo::FindRecord(const CString& Name)
{
    const hash_t h = sdbm_hash((unsigned char *)Name.GetCString(),Name.GetLength());
    for (size_t i = 0; i < m_Records.size(); i++) {
        CDependencyRecord *r = m_Records[i];
        if (r->Hash() == h) {
            if (r->GetName() == Name) {
                return i;
            }
        }
    }
    return -1;
}

size_t CDependencyInfo::AddRecord(const CString& Name)
{
    int Index = FindRecord(Name);
    if (Index>=0) {
        return Index;
    } else {
        CDependencyRecord *r = new CDependencyRecord;
        r->SetName(Name);
        m_Records.push_back(r);
        Index = m_Records.size()-1;
    }
    return Index;
}

bool CDependencyInfo::AddDependency(const size_t Index, const CString& DependencyName)
{
    if (Index<m_Records.size()) {
        size_t dep_index = AddRecord(DependencyName);
        CDependencyRecord *r = m_Records[Index];
        CDependencyRecord *d = m_Records[dep_index];
        r->AddDependency(d);
        d->AddCrossReference(r);
        return true;
    }
    return false;
}

size_t CDependencyInfo::AddDependency(const CString& Name, const CString& DependencyName)
{
    size_t rec_index = AddRecord(Name);
    size_t dep_index = AddRecord(DependencyName);
    CDependencyRecord *r = m_Records[rec_index];
    CDependencyRecord *d = m_Records[dep_index];
    r->AddDependency(d);
    d->AddCrossReference(r);
    return rec_index;
}

void CDependencyInfo::MakeRules(CMakefile& Makefile, const int Section, const bool Multiline)
{
    for (size_t i = 0; i < m_Records.size(); i++) {
        const CDependencyRecord *r = m_Records[i];
        CStringList deps(DirectDependencies(i));
        if (deps.GetCount() > 0) {
            deps.RemoveDuplicates();
            CMakefileRule& rule = Makefile.AddRule(Platform().Pd(r->GetName()),Section);
            rule.Multiline() = Multiline;
            for (int j = 0; j < deps.GetCount(); j++) {
                rule.Dependencies().Insert(Platform().Pd(deps[j]));
            }
        }
    }
}

//------------------------------------------------------------------------------

CIncludeSearchFilter::CIncludeSearchFilter(void)
{
    Clear();
}

CIncludeSearchFilter::CIncludeSearchFilter(const CIncludeSearchFilter& Filter)
{
    Assign(Filter);
}

CIncludeSearchFilter::~CIncludeSearchFilter(void)
{
    Clear();
}

void CIncludeSearchFilter::Clear(void)
{
    m_IncludeDirectories.Clear();
    m_DefinedMacros.Clear();
}

void CIncludeSearchFilter::Show(void)
{
//
}

void CIncludeSearchFilter::Assign(const CIncludeSearchFilter& Filter)
{
    m_IncludeDirectories = Filter.m_IncludeDirectories;
    m_DefinedMacros = Filter.m_DefinedMacros;
}

bool CIncludeSearchFilter::Execute(const CString& FileName, CStringList& Includes)
{
   (void)FileName;(void)Includes;
    return false;
}

bool CIncludeSearchFilter::Execute(const CString& FileName, CDependencyInfo& Dependencies)
{
   (void)FileName;(void)Dependencies;
    return false;
}

void CIncludeSearchFilter::AddIncludeDirectory(const CString& Path)
{
    m_IncludeDirectories.Insert(Path);
}

void CIncludeSearchFilter::AddIncludeDirectories(const CStringList& Paths)
{
    m_IncludeDirectories.Insert(Paths);
}

void CIncludeSearchFilter::AddMacroDefiniton(const CString& Macro)
{
    m_DefinedMacros.Insert(Macro);
}

void CIncludeSearchFilter::AddMacroDefinitons(const CStringList& Macros)
{
    m_DefinedMacros.Insert(Macros);
}

CString CIncludeSearchFilter::ResolveIncludePath(const CString& IncludeName)
{
    CString name = ExtractFileName(IncludeName);
//std::cout<<"include = "<<IncludeName.GetCString()<<std::endl;
//std::cout<<"name = "<<name.GetCString()<<std::endl;
    CString cwd = GetCurrentDir();
    CString path = IncludeTrailingPathDelimiter(MakeNativePath(cwd));
    CString pathname = path+name;
//std::cout<<"checking "<<pathname.GetCString()<<std::endl;
    if (FileExists(pathname)) {
        //std::cout<<"found "<<pathname.GetCString()<<std::endl;
        return name;
    }
//
    for (int i = 0; i < m_IncludeDirectories.GetCount(); i++) {
        path = IncludeTrailingPathDelimiter(MakeNativePath(m_IncludeDirectories[i]));
        pathname = path+name;
        //std::cout<<"checking "<<pathname.GetCString()<<std::endl;
        if (FileExists(pathname)) {
            //std::cout<<"found "<<pathname.GetCString()<<std::endl;
            return pathname;
        }
    }
//std::cout<<"cannot resolve "<<IncludeName.GetCString()<<std::endl;
    return "";
}

//------------------------------------------------------------------------------

CCppIncludeSearchFilter::CCppIncludeSearchFilter(void)
{
//
}

CCppIncludeSearchFilter::CCppIncludeSearchFilter(const CCppIncludeSearchFilter& Filter) :
  CIncludeSearchFilter()
{
    Assign(Filter);
}

CCppIncludeSearchFilter::~CCppIncludeSearchFilter(void)
{
//
}

void CCppIncludeSearchFilter::Assign(const CCppIncludeSearchFilter& Filter)
{
   (void)Filter;
}

bool CCppIncludeSearchFilter::Execute(const CString& FileName, CStringList& Includes)
{
    const int STATE_UNDEFINED         = 0;
    const int STATE_ENTER_COMMENT     = 1;
    const int STATE_CHAR              = 2;
    const int STATE_STRING            = 3;
    const int STATE_BEGIN_DIRECTIVE   = 4;
    const int STATE_ONELINE_COMMENT   = 5;
    const int STATE_MULTILINE_COMMENT = 6;
    const int STATE_EXIT_COMMENT      = 7;
    const int STATE_CHAR_ESC_SEQ      = 8;
    const int STATE_STRING_ESC_SEQ    = 9;
    const int STATE_INCLUDE_DIRECTIVE = 10;
    const int STATE_INCLUDE_FILENAME  = 11;
    const int STATE_END_OF_INPUT      = -1;
    CStringList source;
    if (!source.LoadFromFile(FileName)) return false;
    if (source.GetCount()==0) return false;
//std::cout<<"CWD: "<<GetCurrentDir().GetString()<<std::endl;
//std::cout<<"Searching includes of "<<FileName.GetString()<<"..."<<std::endl;
//ShowStringList("Found includes","include",Includes);
//
    CString include_filename;
    CStringList local_includes;
    for (int l = 0; l < source.GetCount(); l++) {
        CString line(source.GetString(l));
        CStringIterator it(&line);
        int state = STATE_UNDEFINED;
        bool eol = false;
        //while (state!=STATE_END_OF_INPUT)
        while (!eol) {
            switch (state) {
            default:
            case STATE_UNDEFINED: {
                //if (it.This()=='\0') { state = STATE_END_OF_INPUT; break; }
                if (it.This()=='/') {
                    state = STATE_ENTER_COMMENT;
                    break;
                }
                if (it.This()=='\'') {
                    state = STATE_CHAR;
                    break;
                }
                if (it.This()=='"') {
                    state = STATE_STRING;
                    break;
                }
                if (it.This()=='#') {
                    state = STATE_BEGIN_DIRECTIVE;
                    break;
                }
                break;
            }
            case STATE_ENTER_COMMENT: {
                if (it.This()=='/') {
                    state = STATE_ONELINE_COMMENT;
                    break;
                }
                if (it.This()=='*') {
                    state = STATE_MULTILINE_COMMENT;
                    break;
                }
                break;
            }
            case STATE_ONELINE_COMMENT: {
                if (it.This()=='\0') {
                    state = STATE_UNDEFINED;
                    break;
                }
                break;
            }
            case STATE_MULTILINE_COMMENT: {
                if (it.This()=='*') {
                    state = STATE_EXIT_COMMENT;
                    break;
                }
                break;
            }
            case STATE_EXIT_COMMENT: {
                if (it.This()=='/') {
                    state = STATE_UNDEFINED;
                    break;
                }
                state = STATE_MULTILINE_COMMENT;
                break;
            }
            case STATE_CHAR: {
                if (it.This()=='\\') {
                    state = STATE_CHAR_ESC_SEQ;
                    break;
                }
                if (it.This()=='\'') {
                    state = STATE_UNDEFINED;
                    break;
                }
                break;
            }
            case STATE_CHAR_ESC_SEQ: {
                state = STATE_CHAR;
                break;
            }
            case STATE_STRING: {
                if (it.This()=='\\') {
                    state = STATE_STRING_ESC_SEQ;
                    break;
                }
                if (it.This()=='"') {
                    state = STATE_UNDEFINED;
                    break;
                }
                break;
            }
            case STATE_STRING_ESC_SEQ: {
                state = STATE_STRING;
                break;
            }
            case STATE_BEGIN_DIRECTIVE: {
                if (it.Match("include",true)) {
                    state = STATE_INCLUDE_DIRECTIVE;
                    break;
                }
                break;
            }
            case STATE_INCLUDE_DIRECTIVE: {
                if (it.This()=='<') {
                    state = STATE_INCLUDE_FILENAME;
                    break;
                }
                if (it.This()=='"') {
                    state = STATE_INCLUDE_FILENAME;
                    break;
                }
                break;
            }
            case STATE_INCLUDE_FILENAME: {
                if (it.This()=='>') {
                    state = STATE_UNDEFINED;
                }
                if (it.This()=='"') {
                    state = STATE_UNDEFINED;
                }
                if (state == STATE_INCLUDE_FILENAME) {
                    include_filename.Append(it.This());
                } else {
                    local_includes.Insert(include_filename);
                    include_filename.Clear();
                }
                break;
            }
            case STATE_END_OF_INPUT: {
                break;
            }
            }
            eol = (it.This()=='\0');
            it.Next();
        }
    }
    bool result = true;
//ShowStringList("Local includes","include",local_includes);
    for (int i = 0; i < local_includes.GetCount(); i++) {
        CString resolved_include = ResolveIncludePath(local_includes[i]);
        if (!resolved_include.IsEmpty()) {
            if (INVALID_INDEX==Includes.FindString(resolved_include)) {
                Includes.Insert(resolved_include);
                result = result && Execute(resolved_include,Includes);
            }
        }
    }
    Includes.RemoveDuplicates();
    return result;
}

bool CCppIncludeSearchFilter::Execute(const CString& FileName, CDependencyInfo& Dependencies)
{
    const int STATE_UNDEFINED         = 0;
    const int STATE_ENTER_COMMENT     = 1;
    const int STATE_CHAR              = 2;
    const int STATE_STRING            = 3;
    const int STATE_BEGIN_DIRECTIVE   = 4;
    const int STATE_ONELINE_COMMENT   = 5;
    const int STATE_MULTILINE_COMMENT = 6;
    const int STATE_EXIT_COMMENT      = 7;
    const int STATE_CHAR_ESC_SEQ      = 8;
    const int STATE_STRING_ESC_SEQ    = 9;
    const int STATE_INCLUDE_DIRECTIVE = 10;
    const int STATE_INCLUDE_FILENAME  = 11;
    const int STATE_END_OF_INPUT      = -1;
//
    bool result = true;
    CString file_name = MakeNativePath(FileName);
// check if unit is already scanned
    bool scan_unit = true;
    const size_t unit_index = Dependencies.AddRecord(file_name);
    scan_unit = !Dependencies.AreDependenciesComplete(unit_index);
    if (scan_unit) {
        CStringList source;
        if (!source.LoadFromFile(file_name)) return false;
        if (source.GetCount()==0) return false;
        //debug
        //std::cout<<"CWD: "<<GetCurrentDir().GetString()<<std::endl;
        //std::cout<<"Searching includes of "<<FileName.GetString()<<"..."<<std::endl;
        //ShowStringList("Found includes","include",Includes);
        //
        CString include_filename;
        CStringList direct_includes;
        for (int l = 0; l < source.GetCount(); l++) {
            CString line(source.GetString(l));
            CStringIterator it(&line);
            int state = STATE_UNDEFINED;
            bool eol = false;
            //while (state!=STATE_END_OF_INPUT)
            while (!eol) {
                switch (state) {
                default:
                case STATE_UNDEFINED: {
                    //if (it.This()=='\0') { state = STATE_END_OF_INPUT; break; }
                    if (it.This()=='/') {
                        state = STATE_ENTER_COMMENT;
                        break;
                    }
                    if (it.This()=='\'') {
                        state = STATE_CHAR;
                        break;
                    }
                    if (it.This()=='"') {
                        state = STATE_STRING;
                        break;
                    }
                    if (it.This()=='#') {
                        state = STATE_BEGIN_DIRECTIVE;
                        break;
                    }
                    break;
                }
                case STATE_ENTER_COMMENT: {
                    if (it.This()=='/') {
                        state = STATE_ONELINE_COMMENT;
                        break;
                    }
                    if (it.This()=='*') {
                        state = STATE_MULTILINE_COMMENT;
                        break;
                    }
                    break;
                }
                case STATE_ONELINE_COMMENT: {
                    if (it.This()=='\0') {
                        state = STATE_UNDEFINED;
                        break;
                    }
                    break;
                }
                case STATE_MULTILINE_COMMENT: {
                    if (it.This()=='*') {
                        state = STATE_EXIT_COMMENT;
                        break;
                    }
                    break;
                }
                case STATE_EXIT_COMMENT: {
                    if (it.This()=='/') {
                        state = STATE_UNDEFINED;
                        break;
                    }
                    state = STATE_MULTILINE_COMMENT;
                    break;
                }
                case STATE_CHAR: {
                    if (it.This()=='\\') {
                        state = STATE_CHAR_ESC_SEQ;
                        break;
                    }
                    if (it.This()=='\'') {
                        state = STATE_UNDEFINED;
                        break;
                    }
                    break;
                }
                case STATE_CHAR_ESC_SEQ: {
                    state = STATE_CHAR;
                    break;
                }
                case STATE_STRING: {
                    if (it.This()=='\\') {
                        state = STATE_STRING_ESC_SEQ;
                        break;
                    }
                    if (it.This()=='"') {
                        state = STATE_UNDEFINED;
                        break;
                    }
                    break;
                }
                case STATE_STRING_ESC_SEQ: {
                    state = STATE_STRING;
                    break;
                }
                case STATE_BEGIN_DIRECTIVE: {
                    if (it.Match("include",true)) {
                        state = STATE_INCLUDE_DIRECTIVE;
                        break;
                    }
                    break;
                }
                case STATE_INCLUDE_DIRECTIVE: {
                    if (it.This()=='<') {
                        state = STATE_INCLUDE_FILENAME;
                        break;
                    }
                    if (it.This()=='"') {
                        state = STATE_INCLUDE_FILENAME;
                        break;
                    }
                    break;
                }
                case STATE_INCLUDE_FILENAME: {
                    if (it.This()=='>') {
                        state = STATE_UNDEFINED;
                    }
                    if (it.This()=='"') {
                        state = STATE_UNDEFINED;
                    }
                    if (state == STATE_INCLUDE_FILENAME) {
                        include_filename.Append(it.This());
                    } else {
                        direct_includes.Insert(include_filename);
                        include_filename.Clear();
                    }
                    break;
                }
                case STATE_END_OF_INPUT: {
                    break;
                }
                }
                eol = (it.This()=='\0');
                it.Next();
            }
        }
        //
        //ShowStringList("Direct includes","include",direct_includes);//debug
        Dependencies.SetDependenciesComplete(unit_index);
        int resolved_count = 0;
        for (int i = 0; i < direct_includes.GetCount(); i++) {
            CString resolved_include = ResolveIncludePath(direct_includes[i]);
            //std::cout<<direct_includes[i].GetCString()<<" -> "<<resolved_include.GetCString()<<std::endl;
            if (!resolved_include.IsEmpty()) {
                Dependencies.AddDependency(unit_index,resolved_include);
                resolved_count++;
                int resolved_index = Dependencies.FindRecord(resolved_include);
                if (!Dependencies.AreDependenciesComplete(resolved_index)) {
                    result = result && Execute(resolved_include,Dependencies);
                }
            }
        }
        //no need to check this: recursive search will do the job
        //if (0==resolved_count)
        //{
        //Dependencies.SetDependenciesComplete(unit_index);
        //}
    }
    return result;
}

//------------------------------------------------------------------------------
