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

 Copyright 2012 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 *************************************/

#ifndef Log_H
#include "Log.h"
#endif

#ifndef Folder_H
#include "Folder.h"
#endif

#ifndef IconClass_H
#include "IconClass.h"
#endif

#ifndef IconFactory_H
#include "IconFactory.h"
#endif

#ifndef RootFolder_H
#include "RootFolder.h"
#endif

#ifndef Items_H
#include "Items.h"
#endif

#ifndef FolderObserver_H
#include "FolderObserver.h"
#endif

#ifndef FolderWindow_H
#include "FolderWindow.h"
#endif

#ifndef Items_H
#include "Items.h"
#endif



Folder::Folder(Folder* parent,const IconClass& kind,
	const string& name,IconInfo* info):
	IconObject(parent,kind,name,info),
	ready_(false)
{
	Items::fix(this);
}

Folder::~Folder()
{
}

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 *o = find(name);
	return o == 0 ? IconFactory::create(this,name) : o;
}

IconObject* Folder::find(const vector<string>& names)
{

	//cout << "Folder::find " << names.size() << endl;
	//for(int i = 0; i < names.size() ; i++)
		//cout << '|' << names[i] << '|';
	//cout << endl;

	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;

}

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();
	}
}

void Folder::scan()
{

	ready_ = true;

	set<string> files = path().files();
	
	for(set<string>::iterator i = files.begin(); i != files.end() ; ++i)
		create(*i);

	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);

}

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

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

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

Folder* Folder::folder(const string& name,bool create)
{
	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)
{
	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;
}

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))
	{
		Log::error(o) << "Oops loops" << endl;
		return false;
	}

	kids_[o->name()] = o;

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

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

	return true;
}

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

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

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

	return true;
}

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


void Folder::ensureChildrenWritable() 
{	    
	if(!ready_)
		scan();

	for(KidMap::iterator j = kids_.begin(); j != kids_.end(); ++j)
	{
		(*j).second->path().makeWritableByUser();
		(*j).second->dotPath().makeWritableByUser();
	}
}


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

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

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

	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)
	{
		int 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;

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

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);
	}

}

void Folder::open()
{
	FolderWindow::open(this);
}

void Folder::close()
{
	FolderWindow::close(this);
}

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;
}

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)
{
	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()
{
  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()
{
	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)
{
	if(!locked())
	{
		o->position(0,0);
		adopt(o);
		parent()->tellObservers(&FolderObserver::highlight,this);
	}
}

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