/***************************** LICENSE START ***********************************

 Copyright 2014 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "Folder.h"

#include "IconClass.h"
#include "IconFactory.h"
#include "RootFolder.h"
#include "Items.h"
#include "FolderInfo.h"
#include "FolderSettings.h"
#include "FolderObserver.h"
#include "Request.h"
///#include "FolderWindow.h"

//#define DEBUG_FOLDER__

vector<Folder*> Folder::folders_;
const unsigned int Folder::maxNokidsNum_ = 100;

//Path stores the full path aka fullName
//Name stores the directory name

Folder::Folder(Folder* p, const IconClass& kind,
               const string& name, IconInfo* info) :
    IconObject(p, kind, name, info),
    ready_(false),
    scanIsOn_(false),
    folderInfo_(0),
    settings_(0)

{
}

Folder::~Folder()
{
    if (folderInfo_)
        delete folderInfo_;
    if (settings_)
        delete settings_;
}


FolderInfo* Folder::folderInfo()
{
    if (!folderInfo_)
        folderInfo_ = new FolderInfo(this);

    return folderInfo_;
}

FolderSettings* Folder::settings()
{
    if (!settings_)
        settings_ = new FolderSettings(this);

    return settings_;
}

int Folder::numOfIconKids()
{
    int cnt = 0;
    for (KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
        if (!j->second->isFolder())
            cnt++;
    return cnt;
}


void Folder::saveFolderInfo()
{
    if (!locked_)
        folderInfo()->write();
}

void Folder::addObserver(FolderObserver* o)
{
    if (o) {
        if (!observers_.insert(o).second)
            return;
        //if(!ready_)
        //	scan();
        //else
        //	for(KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
        //		o->arrived((*j).second);
    }
}

void Folder::removeObserver(FolderObserver* o)
{
    observers_.erase(o);
}

IconObject* Folder::create(const string& name)
{
    IconObject* obj = find(name);
    if (obj == 0) {
        if (noKids_.find(name) == noKids_.end()) {
            obj = IconFactory::create(this, name);
            if (obj == 0) {
                noKids_.insert(name);
            }
        }
    }

    return obj;
}

IconObject* Folder::find(const vector<string>& names)
{
#ifdef DEBUG_FOLDER__
    cout << "Folder::find " << names.size() << endl;
    for(int i = 0; i < names.size() ; i++)
        cout << '|' << names[i] << '|';
    cout << endl;
#endif
    if (names.size() == 0)
        return this;

    if (names.size() == 1)
        return find(names[0]);

    vector<string> rest(names.begin() + 1, names.end());
    IconObject* s = find(names[0]);

    return s ? s->find(rest) : 0;
}

IconObject* Folder::find(const string& name)
{
    if (!ready_)
        scan();

    KidMap::iterator j = kids_.find(name);

    if (j == kids_.end())
        return 0;
    else
        return (*j).second;
}

#if 0
/*void Folder::edit()
{
	open();


	// if the user wants to keep just one folder view open, then
	// close the parent one

	MvRequest myPref = MvApplication::getPreferences();
	const char* openEachFolder = myPref( "OPEN_EACH_FOLDER" );

	if ( (openEachFolder != NULL) && (strcmp( openEachFolder, "In Same Window") == 0))
	{
		if (parent_) parent_->close();
	}
}*/

#endif

/*string Folder::fullName() const
{
  	return path_.str(); 
}  

Path Folder::path() const
{
	return path_;
}*/


void Folder::scan()
{
    ready_    = true;
    scanIsOn_ = true;

    //cout << "Scan for: " << fullName() << endl;

    set<string> files = path().files();

    //Create and iconObject for each file/dir with the exception of the
    //dirs ending with a # character.
    for (set<string>::iterator i = files.begin(); i != files.end(); ++i) {
        //if((*i).rfind("#") != (*i).size()-1)
        //{
        create(*i);
        //}
    }

    //Remove non-existing icons

    vector<IconObjectH> erase;

    for (KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
        if (files.find((*j).first) == files.end())
            erase.push_back((*j).second);

    for (vector<IconObjectH>::iterator k = erase.begin(); k != erase.end(); ++k)
        release(*k);

    saveFolderInfo();
    scanIsOn_ = false;

    //Clear nokids if they overgrew
    if (noKids_.size() > maxNokidsNum_)
        noKids_.clear();
}


void Folder::scanForNewFile(const string& name, int x, int y)
{
    ready_    = true;
    scanIsOn_ = true;

    IconObject* obj = find(name);
    if (obj == 0) {
        IconFactory::create(this, name, x, y);
    }

    saveFolderInfo();
    scanIsOn_ = false;
}

void Folder::recheckKidsIconClass()
{
    bool changed = false;
    for (KidMap::iterator it = kids_.begin(); it != kids_.end(); ++it) {
        IconObjectH obj = (*it).second;
        if (obj->recheckIconClass()) {
            changed = true;
            for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
                (*j)->modified(obj);
        }
    }

    if (changed) {
        saveFolderInfo();
    }
}

void Folder::recheckKidIconClass(IconObject* obj)
{
    for (KidMap::iterator it = kids_.begin(); it != kids_.end(); ++it) {
        if ((*it).second == obj) {
            if (obj->recheckIconClass()) {
                for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j) {
                    (*j)->modified(obj);
                }
                saveFolderInfo();
            }
            return;
        }
    }
}

#if 0
void Folder::visit(FolderVisitor& visitor) 
{	    
	if(!ready_)
		scan();

 	 for(KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
	 	visitor.visit((*j).second);
}

//MODIF
Folder* Folder::top()
{
  	return 0;
	//static RootFolder root;
	//return &root;
}
#endif

#if 0  
Folder* Folder::folder(const string& name,bool create)
{

  
  
 return 0;

 	Folder* f;

	switch(name[0])
	{
		case 0:
			return top();
			//break;

		case '/':
			f = dynamic_cast<Folder*>(IconObject::search(name));
			if(f == 0 && create)
				f = dynamic_cast<Folder*>(IconFactory::create(name,IconClass::find("FOLDER")));
			return f;
			//break;

		default:
		   return dynamic_cast<Folder*>(Items::find(name));
		   //break;
	}
}

Folder* Folder::folder(const string& name1,const string& name2,bool create)
{
	return 0;
	
	Folder* f = folder(name1,create);
	return f?folder(f->fullName()+"/"+name2,create):0;
}

Folder* Folder::folder(const string& name1,const string& name2,
	const string& name3,bool create)
{
	return 0;
	
	Folder* f = folder(name1,name2);
	return f?folder(f->fullName()+"/"+name3):0;
}

#endif

bool Folder::adopt(IconObject* o)
{
    if (find(o->name()) != 0) {
        // Log::error(o) << "Icon already exists" << endl;
        return false;
    }

    Folder* f = dynamic_cast<Folder*>(o);
    if (f && f->ancestor(this)) {
        marslog(LOG_WARN, "Folder::adopt - Infinite loop when trying to adopt icon=%s", o->name().c_str());
        return false;
    }

    f = o->parent();
    if (!f && o->iconClass().type() != "Folder") {
        marslog(LOG_WARN, "Folder::adopt - Cannot adopt icon object without a parent! icon=%s", o->name().c_str());
        return false;
    }

    kids_[o->name()] = o;
    folderInfo()->add(o->name(), &o->info());

    if (f != this) {
        o->reparent(this);
        if (f)
            f->release(o);
    }

    for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
        (*j)->arrived(o);

    if (!scanIsOn_)
        saveFolderInfo();

    //If we have not scanned the folder yet we nned to do it now!
    if (!ready_) {
        scan();
    }

    return true;
}

bool Folder::release(IconObject* o)
{
    if (find(o->name()) == 0) {
        // Error("icon does not exist exists");
        return false;
    }

    kids_.erase(o->name());
    folderInfo()->remove(o->name());

    for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
        (*j)->gone(o);

    saveFolderInfo();

    return true;
}

#if 0
void Folder::createFiles()
{
//MODIF
	return;
	IconObject::createFiles();
	path().mkdir();
}

set<string> Folder::can()
{
	set<string> c = IconObject::can();
	c.erase("examine");
	return c;
}
#endif

void Folder::createFiles()
{
    IconObject::createFiles();
    if (!path().exists())
        path().mkdir();
}


string Folder::uniqueName(const string& name)
{
    char buf[1024];
    int i = 0;

    if (kids_.find(name) == kids_.end())
        return name;

    Path p(name);
    string fName;
    string fSuffix;
    p.nameAndSuffix(fName, fSuffix);

    //For macro we want to keep the suffix
    if (fSuffix == "mv") {
        do {
            sprintf(buf, "%s_%d.%s", fName.c_str(), ++i, fSuffix.c_str());
        } while (kids_.find(buf) != kids_.end());
    }
    else {
        do {
            sprintf(buf, "%s %d", name.c_str(), ++i);
        } while (kids_.find(buf) != kids_.end());
    }

    return buf;
}


string Folder::duplicateName(const string& name)
{
    char buf[1024];
    int i = 0;
    int d = 0;

    if (name.find("Copy ") == 0) {
        std::size_t n = name.find(" of ");
        if (n != string::npos) {
            string s = name.substr(5, n - 5);
            if (is_number(s.c_str()))
                d = n + 4;
        }
    }

    do {
        sprintf(buf, "Copy %d of %s", ++i, name.c_str() + d);
    } while (kids_.find(buf) != kids_.end());

    return buf;
}


void Folder::position(IconObject* o, int x, int y)
{
    for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
        (*j)->position(o, x, y);

    if (observers_.size() == 0)
        o->position(x, y);
}


void Folder::renamed(IconObject* o, const string& old_name, const string& new_name)
{
    IconObjectH save = o;
    kids_.erase(old_name);
    kids_[new_name] = o;

    folderInfo()->rename(old_name, new_name);

    for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
        (*j)->renamed(o, old_name);

    saveFolderInfo();
}

void Folder::tellObservers(TellObserverProc proc, IconObject* o)
{
    IconObjectH save = o;
    for (set<FolderObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j) {
        FolderObserver* f = *j;
        (f->*proc)(o);
    }
}

#if 0

void Folder::open()
{
//MODIF
return;
	//FolderWindow::open(this);
}

void Folder::close()
{
//MODIF
return;
	//FolderWindow::close(this);
}


#endif

bool Folder::ancestor(IconObject* o)
{
    if (o == this)
        return true;

    for (KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j) {
        IconObject* p = (*j).second;
        if (o == p)
            return true;

        Folder* f = dynamic_cast<Folder*>(p);
        if (f && f->ancestor(o))
            return true;
    }

    return false;
}

bool Folder::descendant(Folder* f)
{
    if (!f)
        return false;
    if (f == this)
        return true;
    return (parent()) ? parent()->descendant(f) : false;
}

#if 0

class FolderCopier : public FolderVisitor {
	FolderH target_;
	virtual void visit(IconObject* o) { 
		if(!target_->find(o->name()))
			o->clone(target_, false); 
	}
public:
	FolderCopier(Folder* f) : target_(f) {}
};

void Folder::copyContent(Folder* from)
{
//MODIF
return;
	
	FolderCopier cp(this);
	if(from) from->visit(cp);
}


class FolderCompare : public FolderVisitor {
	FolderH other_;
	virtual void visit(IconObject* o);
	bool same_;
public:
	FolderCompare(Folder* f) : other_(f),same_(true) {}
	bool same() const { return same_; }
};

void FolderCompare::visit(IconObject* o)
{
	if(!same_) return;

	IconObject* x = other_->find(o->name());
	if(x == 0) 
		same_ = false;
}

bool Folder::sameAs(IconObject* other)
{
	Folder* f = dynamic_cast<Folder*>(other);
	if(f == 0) return false;

	FolderCompare cmp1(this);
	f->visit(cmp1);

	FolderCompare cmp2(f);
	this->visit(cmp2);

	return cmp1.same() && cmp2.same();

}

void Folder::notifyChanged()
{
	// When a folder changes, (its name... )
	// mark all icons changed as well

	IconObject::notifyChanged();

	for(KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
	{
		IconObject *p = (*j).second;
		p->notifyChanged();
	}
}

void Folder::destroy()
{
 //MODIF
 return;
 
 if( ! isLink() )     //-- do not delete contents if link!
    {
	//-- IBM/AIX/xlC+STL loops with this construct:
	//for(KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
	//{
	//	IconObject *p = (*j).second;
	//	p->destroy();
	//}

	//-- this one ok for xlC+STL:
	KidMap::iterator j = kids_.begin();
	while( j != kids_.end() )
	{
		IconObject *p = (*j).second;
		++j;
		p->destroy();
	}
    }

  IconObject::destroy();
}

void Folder::empty()
{
//MODIF
return;

	scan();
	KidMap kids = kids_;
	for(KidMap::iterator j = kids.begin(); j != kids.end(); ++j)
	{
		IconObject *p = (*j).second;
		p->toWastebasket();
	}
	scan();

}

void Folder::drop(IconObject* o)
{
//MODIF
return;

if(!locked())
	{
		o->position(0,0);
		adopt(o);
		parent()->tellObservers(&FolderObserver::highlight,this);
	}
}

#endif


void Folder::destroy()
{
    if (!isLink())  //-- do not delete contents if link!
    {
        //-- IBM/AIX/xlC+STL loops with this construct:
        //for(KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
        //{
        //	IconObject *p = (*j).second;
        //	p->destroy();
        //}

        //-- this one ok for xlC+STL:
        KidMap::iterator j = kids_.begin();
        while (j != kids_.end()) {
            IconObject* p = (*j).second;
            ++j;
            p->destroy();
        }
    }

    IconObject::destroy();
}

void Folder::iconClasses(map<string, IconObjectH>& res)
{
    KidMap::iterator j = kids_.begin();
    while (j != kids_.end()) {
        res[(*j).second->className()] = (*j).second;
        ++j;
    }
}

set<string> Folder::can()
{
    set<string> c = IconObject::can();
    c.erase("edit");
    c.erase("log");
    c.insert("open");
    c.insert("openInTab");
    c.insert("openInWin");
    c.insert("bookmark");
    return c;
}

Request Folder::request() const
{
    return Request("");
}

void Folder::request(const Request&)
{
}

//========================================================
//
//  Static methods
//
//========================================================

Folder* Folder::top()
{
    static RootFolder root;
    return &root;
}


Folder* Folder::folder(const string& name, bool create)
{
    Folder* f;

    if (name == "/")
        return top();

    switch (name[0]) {
        case 0:
            return 0;
            //return top();
            //break;

        case '/':
            f = dynamic_cast<Folder*>(IconObject::search(name));
            if (f == 0 && create)
                f = dynamic_cast<Folder*>(IconFactory::create(name, IconClass::find("FOLDER")));
            return f;
            //break;

        default:
            return dynamic_cast<Folder*>(Items::find(name));
            //break;
    }
}
/*  
Folder* Folder::folder(const string&path,bool create) 
{
        if(path.empty()) return 0;
	
	if(path.find("/") != string::npos)
	{
		for(vector<Folder*>::iterator it=Folder::folders_.begin(); it != Folder::folders_.end(); it++)
		{
	  		if((*it)->path().str() == path)
			{
		  		return *it;
			} 
		}
		
		if(create)
		{
			IconObject* obj = IconFactory::create(0,path,IconClass::find("FOLDER"));	
			if(obj)
			{  
				return static_cast<Folder*>(obj);
			}
		}	
	}	
	else
	{  
		return static_cast<Folder*>(Items::find(path));
	}
	
	return 0;
}*/

Folder* Folder::folder(const string& name1, const string& name2, bool create)
{
    Folder* f = folder(name1, create);
    return f ? folder(f->fullName() + "/" + name2, create) : 0;
}

Folder* Folder::folder(const string& name1, const string& name2,
                       const string& name3, bool /*create*/)
{
    Folder* f = folder(name1, name2);
    return f ? folder(f->fullName() + "/" + name3) : 0;
}

IconObject* Folder::icon(const string& /*fullName*/)
{
    /*Path p(fullName);
	Folder *f=Folder::folder(p.directory().str());
	
	IconObject *obj=0;
	
	if(f)
	{
	  	obj=f->find(p.name());
		if(!obj)
		{
			f->scan();
			obj=f->find(p.name());
		}	
	}
	
	else
	{
	  	obj=IconFactory::create(p.directory().str(),IconClass::find("FOLDER"));
		if(obj)
		{
		  	f=static_cast<Folder*>(obj);
		  	obj=f->find(p.name());
		}	
	}
	
	return obj;*/
    return 0;
}

static IconMaker<Folder> maker1("FOLDER");
static IconMaker<Folder> maker2("Folder");
