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

 Copyright 2013 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 "Log.h"

#include "IconObject.h"

#include "IconClass.h"
#include "IconInfo.h"
#include "IconFactory.h"

#include "State.h"
#include "Folder.h"
#include "Editor.h"
#include "Service.h"
#include "Command.h"
#include "Language.h"
#include "Request.h"
#include "Queue.h"
#include "Parameter.h"
#include "Path.h"
#include "FolderObserver.h"
#include "Tokenizer.h"
#include "Dependancy.h"
#include "MvQLogDialog.h"

#include <algorithm>
#include <sstream>
#include <assert.h>

#include "Metview.h"

#include <sys/stat.h>

vector<IconObject*> IconObject::objects_;


IconObject::IconObject(Folder* parent,const IconClass& kind,const string& name,
		IconInfo* info):
	name_(name),
	parent_(parent),
	class_(kind),
	info_(info),
	queue_(0),
	log_(0),
	editor_(0),
	logWindow_(0),
	locked_(false),
	link_(false),
	status_(DefaultStatus)
{
	string p;
	if(parent_)  
	{
		parent_->attach();
		p = path().str();
	}
	else
	{
	  	p=name;
	}
	
	struct stat s;
	if(lstat(p.c_str(),&s) == 0)
	{
		link_ = S_ISLNK(s.st_mode);
		locked_ = faccess(p.c_str(),W_OK);
		//else locked_ = true;
	}
		//else locked_ = true;
	
	objects_.push_back(this);
}

IconObject::~IconObject()
{	  
	reset(); 
	if ( parent_ )  parent_->detach();
	delete info_;
	
	
	vector<IconObject*>::iterator it;
	for(it=objects_.begin(); it != objects_.end(); it++)
	{
	  	if(*it == this)		  
		{
		  	objects_.erase(it);
			break;
		}	
	}	
}

const string& IconObject::name() const
{
	return name_;
}

Folder* IconObject::parent() const
{
	return parent_;
}

IconInfo& IconObject::info() const
{
	if(info_ == 0)
	{
		Request info("USER_INTERFACE");
		info("ICON_CLASS") = className().c_str();

		IconObject* o = const_cast<IconObject*>(this);
		o->info_ = new IconInfo(info);
		o->saveInfo();
	}

	return *info_;
}

string IconObject::fullName() const
{
	string p=parent()->fullName();
	if(p != "/")
	  	return p + "/"+ name();
	else
	  	return p + name();
}

Path IconObject::path() const
{
	return parent()->path().add(name());
}

string IconObject::dotName(const string& n) const
{
	return string(".") + n;
}

Path IconObject::dotPath() const
{
	return parent()->path().add(dotName(name()));
}

string IconObject::embeddedName(const string& n) const
{
	return  n + "#";
}

Path IconObject::embeddedPath() const
{
	return parent()->path().add(embeddedName(name()));
}

Folder* IconObject::embeddedFolder(const string& n,bool create) const
{	
	return Folder::folder(parent()->fullName(),embeddedName(name()),n,create);
}

Folder* IconObject::embeddedFolder(bool create) const
{
	return Folder::folder(parent()->fullName(),embeddedName(name()),create);
}

bool IconObject::isEmbedded()
{
	//Needs to be improved!
	return (parent()->path().str().find("#") != string::npos);
}	  

const string& IconObject::className() const
{
	return iconClass().name();
}

const IconClass& IconObject::iconClass() const
{
	return class_;
}

const IconClass& IconObject::editorClass() const
{
	return iconClass();
}

void IconObject::doubleClick()
{	
  	edit();
}

void IconObject::print(ostream& s) const
{
	s << className() << " - " << fullName();
}


void IconObject::command(const string& name)
{
	Command::execute(name,this);
}

Task* IconObject::action(const Action& a)
{
	Service* s = iconClass().service(a);
	if(s == 0) s = Service::find("InternalDesktopService");

	if(s != 0) {
		Task* t = s->task(a,this);
		if(t != 0)
		{
			if(queue_ == 0) queue_ = new Queue(this);
			queue_->push(t);
			return t;
		}
	}
	return 0;
}


Log& IconObject::log()
{
	if(log_ == 0) log_ = new Log(this);
	return *log_;
}

Path IconObject::logPath()
{
	return log().path();
}

/*Request IconObject::request() const
{
	cout << "Oops, should not be called..." << endl;
	return Request("");
}

void IconObject::request(const Request&)
{
	cout << "Oops, should not be called..." << endl;
}*/

Request IconObject::fullRequest() const
{
	return request();
}

Request IconObject::requestForVerb(const string& verb) const
{
	Request r=request();
	if(r.getVerb() == verb)
	  	return r;
	
	return Request();
}  

Language& IconObject::language() const
{
	return iconClass().language();
}

IconObject* IconObject::search(const string& fullName)
{
	//cout << "search " << fullName << endl;
		
	//return Folder::icon(fullName);
	
	if(fullName[0] != '/') return 0;

	Tokenizer parse("/");
	vector<string> n;
	parse(fullName,n);

	vector<string> names; 

	for(vector<string>::iterator j = n.begin(); j != n.end(); ++j)
	{
		if(*j == "" || *j == ".") 	
			continue;

		if(*j == "..")
			names.pop_back();
		else
			names.push_back(*j);
	}

	return Folder::top()->find(names);
}

IconObject* IconObject::search(IconObject& /*o*/,const string& /*relativeName*/)
{
	return 0;
}

IconObject* IconObject::find(const vector<string>& n)
{
	return (n.size() == 0) ? this : 0;
}

IconObject* IconObject::find(const string& /*name*/)
{
	return 0;
}	

void IconObject::saveInfo()
{
	//info().save(dotPath());
}

void IconObject::reparent(Folder* parent)
{
	if(parent_ != parent)
	{
		modified();

		Path old_path = path();
		Path old_emb  = embeddedPath();

		parent_->detach();
		parent_ = parent;		
		parent_->attach();

		Path new_path = path();
		Path new_emb  = embeddedPath();

		old_path.rename(new_path);
		if(old_emb.exists())
			old_emb.rename(new_emb);

		modified();
	}
}

bool IconObject::checkNewName(const string& new_name)
{
  	if(name_ == new_name)
		return false;

	if(!renamable())
	{
		 Log::error(this) << "Object cannot be renamed" << endl;
		 return false;
	}

	if(parent()->find(new_name))
	{
		Log::error(this) << "Cannot rename " << name() << " to " << new_name << ", an icon already exists" << endl;
		return false;
	}

	if( (new_name[0] == '.' && name_[0] != '.') 
		|| new_name.find("/") != string::npos 
		|| new_name.length()  == 0)
	{
		Log::error(this) << "Cannot rename " << name() << " to " << new_name << ", invalid name" << endl;
		return false;
	}
	
	return true;
}  


bool IconObject::rename(const string& new_name)
{
	if(!checkNewName(new_name))
	  	return false;

	modified();

	string old_name = name_;
	string old_embedded_name=embeddedName(name_);

	Path old_path = path();
	Path old_embedded_path = embeddedPath();	
	Folder* old_emb  = embeddedFolder(false);

	name_ = new_name;
	
	Path new_path = path();
	old_path.rename(new_path);
	
	//If there is embedded folder
	if(old_embedded_path.exists())
	{
		if(old_emb)
		{
			old_emb->rename(embeddedName(name())); 
		}
		else
		{
		  	old_embedded_path.rename(embeddedPath());
		}
	}
	
	parent()->renamed(this,old_name,new_name);
	modified();

	return true;
}

void IconObject::position(int x,int y)
{
    info().position(x,y);
    saveInfo();
}

void IconObject::createFiles()
{
}

set<string> IconObject::can()
{
  	set<string> c = iconClass().can();
	
	if(log_ != 0) c.insert("log");
	
	if(parent()->descendant(Folder::folder("wastebasket")))
		c.erase("delete");
	else 	
		c.erase("destroy");
	
	if(locked() && !isLink())
	{
	  	c.erase("cut");
	  	c.erase("delete");
		c.erase("destroy");
		c.erase("rename");
	}	
	
	if(isBrokenLink())
	{
		c.erase("duplicate");
		c.erase("copy");
		c.erase("rename");
	}
	
	/*Folder *f=parent();
	if(f && f->locked())
	{
	  	c.erase("duplicate");
	  	c.erase("delete");
		c.erase("cut");
		c.erase("destroy");
		c.erase("compress");
	}*/	
	
	if(c.find("save") == c.end())
	 	c.erase("clear"); 
	

	c.insert("remove");
	//c.insert("output");
	c.insert("mergeIcon");
	c.insert("replaceIcon");
	
	return c;
}

//-------------------------------------------------------------------------
void IconObject::toWastebasket()
{
	Folder* f = Folder::folder("wastebasket");
	if(f == 0)
	{
	  	Log::error(this) << "Can't find wastebasket!" << endl;
	}
	else 
	{
		//position(0,0);
		f->adopt(this);
	}
}


void IconObject::edit()
{
	set<string> c = iconClass().can();
  	if(c.find("edit") != c.end())
	{
  		Editor::open(this);
	}
}

void IconObject::edit2()
{
	//Editor::open(this,"ExternalTextEditor");
}

void IconObject::showLog()
{
	MvQLogDialog::open(this);
}

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

void IconObject::duplicate()
{
	clone(parent(),info().x()+20,info().y()+20,false);
	//parent()->position(c,info().x()+20,info().y()+20);
}

IconObject* IconObject::clone(Folder* f,bool update)
{
	return clone(f,IconInfo::undefX(),IconInfo::undefY(),update);
}	
  
IconObject* IconObject::clone(Folder* f,int x, int y, bool update)
{
	//createFiles();
	
	string new_name;
	
	f->scan();
		
	if (update)
		new_name = name();
	else if (f == parent())
		new_name = f->duplicateName(name());
	else
		new_name = f->uniqueName(name());
	
	Path old_path = path();
	Path old_emb  = embeddedPath();

	Path new_path = f->path().add(new_name);
	Path new_emb  = f->path().add(embeddedName(new_name));

	old_path.copy(new_path);
	if(old_emb.exists())
	{
	  	old_emb.copy(new_emb);
	}	
		
	IconObject *obj=IconFactory::create(f,new_name,class_,x,y);
	
	return obj;
}


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

void IconObject::editor(Editor* e)
{
	if(editor_ != e)
	{
		editor_ = e;
		if(e) 
			notifyOpened();
		else 
			notifyClosed();
	}
}

Editor* IconObject::editor()
{
	return editor_;
}

void IconObject::raiseEditor()
{
	if(editor_)
		editor_->raiseIt();
}	

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

void IconObject::logWindow(MvQLogDialog* e)
{
	logWindow_ = e;
}

MvQLogDialog* IconObject::logWindow()
{
	return logWindow_;
}

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

void IconObject::removeFiles()
{
	modified();
	path().remove();
	if(embeddedPath().exists())
		embeddedPath().remove();
}

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

void IconObject::modified()
{
	MvApplication::notifyIconModified(
		fullName().c_str(),
		className().c_str());

	// Forget any outstanding task
	reset();
	notifyChanged();
}

void IconObject::created()
{
	MvApplication::notifyIconCreation(
		fullName().c_str(),
		className().c_str());
}

string IconObject::dropText() const
{
	Request r = request();
	return request2string(r);
}

string IconObject::relativeName(IconObject* other) const
{
	return relpath(fullName().c_str(),other->fullName().c_str());
}

string IconObject::makeFullName(const string& name) const
{
	return makepath(parent()->fullName().c_str(),name.c_str());	
}

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

void IconObject::reset()
{
	if(logWindow_) logWindow_->reject();

	if(log_)
	{
	  	delete log_;
		log_ = 0;
	}	

	if(queue_) queue_->ownerGone();
	queue_	= 0;

	dependancies_.clear();
}

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

void IconObject::notifyWaiting()
{
	status_=WaitingStatus;
	if(parent_)
	  	parent()->tellObservers(&FolderObserver::waiting,this);
}

void IconObject::notifyError()
{
	status_=ErrorStatus;
	if(parent_)
	  	parent()->tellObservers(&FolderObserver::error,this);	
}

void IconObject::notifyDestroyed()
{
	for(set<IconObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
 		(*j)->iconDestroyed(this);
}

void IconObject::notifyChanged()
{
	status_=DefaultStatus;
	parent_->tellObservers(&FolderObserver::modified,this);
	
	for(set<IconObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
		(*j)->iconChanged(this);
}

void IconObject::notifyReady()
{
	status_=ReadyStatus;
  	if(parent_)
  		parent_->tellObservers(&FolderObserver::ready,this);	
}
void IconObject::notifyOpened()
{
	if(parent_)
		parent_->tellObservers(&FolderObserver::opened,this);	
}
void IconObject::notifyClosed()
{
	if(parent_)
		parent_->tellObservers(&FolderObserver::closed,this);	
}



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

void IconObject::addObserver(IconObserver* o)
{
	if(observers_.find(o) == observers_.end())
	  	observers_.insert(o);
}

void IconObject::removeObserver(IconObserver* o)
{
	if(observers_.find(o) != observers_.end())
	  	observers_.erase(o);
}

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

const set<DependancyH>& IconObject::dependancies()
{
	return dependancies_;
}

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

void IconObject::destroy()
{
	IconObjectH save = this;
	removeFiles();	
	parent()->release(this); //this notify the folderobservers!!
	
	//Do we need it?
	notifyDestroyed();
	
	if(editor_) editor_->empty();
}

void IconObject::empty()
{
	// Not called, obly wastebasket
}

bool IconObject::renamable() const
{
	//return !locked() && !parent()->locked() && editor_ == 0;
	return !locked() && !parent()->locked();
}

bool IconObject::locked() const
{
	return locked_;
}

void IconObject::lock()
{
	locked_ = true;
}

bool IconObject::temporary() const
{
	return false;
}

//=================================================================

vector<IconObjectH> IconObject::subObjects(const string& param,const Request& r)
{ 
	int i = 0;
	bool more = false;
	vector<IconObjectH> result;

	do 
	{
		MvRequest   a = r(param.c_str(),i);
		const char* b = r(param.c_str(),i);

		more = false;
		IconObjectH o = 0;

		if(a)
		{
			a.print();
			if(b)
			  	cout << b << endl;
			
			more = true;
			// Embbeded object:
			// We need to create a temporary

			cout << "Embedded object" << endl;
			o = IconFactory::create(this,a);

		}

		if(b && strcmp(b,"#") )
		{  
			more = true;
			// Real object
			string name = makeFullName(b);
			o = search(name);
			if(o == 0) cout << "Cannot find " << name << endl;
						
		}

		if(o != 0) result.push_back(o);

		i++;

	} while(more);

	return result;	
}


void IconObject::setSubObjects(const string& name,
	const vector<IconObjectH>& sub,Request& r,bool clean)
{
	//Delete the unused embedded subObjects
  	if(clean)
	{
		vector<IconObjectH> oriSub = subObjects(name,r);
		for(vector<IconObjectH>::const_iterator j = oriSub.begin(); j != oriSub.end(); ++j)	
		{
	  		IconObject* o = *j;
			if(o && o->isEmbedded())
			{
		  		if(std::find(sub.begin(),sub.end(),o) == sub.end())
				{
			  		o->destroy();
				}	
			}
		}	
	}	  
	
	r.unsetParam(name.c_str());

	// We cannot mix embedded objects and real objects

	for(vector<IconObjectH>::const_iterator j = sub.begin(); j != sub.end(); ++j)
	{
		IconObject* o = *j;
		if(o->temporary())
		{
			r(name.c_str()) += o->request();			
		}
		else
		{
			string rel = relativeName(o);
			r(name.c_str()) += rel.c_str();
			cout << "subObjects " << *this << endl;
			cout << " subObjects " << *o << endl;
			cout << " subObjects " << rel << endl;
		}
	}
	
	r.print(); 

}

bool IconObject::visible() const
{
	return name_[name_.length() - 1] != '#';
}

bool IconObject::sameAs(IconObject* other)
{
	if(&iconClass() != &other->iconClass())
		return false;

	Path p1 = this->path();
	Path p2 = other->path();

	FILE *f1 = fopen(p1.str().c_str(),"r");
	FILE *f2 = fopen(p2.str().c_str(),"r");

	bool same = f1 && f2;

	while(same )
	{
		int c1 = getc(f1);
		int c2 = getc(f2);
		same = (c1 == c2);
		if(c1 == EOF || c2 == EOF) break;
	}

	if(f1) fclose(f1);
	if(f2) fclose(f2);

	return same;
}

void IconObject::touch()
{
	path().touch();
}

bool IconObject::isLink() const
{
	return link_;
}

bool IconObject::isBrokenLink() const
{
  	if(!link_)
	  	return false;
	else
	{
		return !path().exists();
	}
}

bool IconObject::isInTimer() const
{
	return false;
}

void IconObject::drop(IconObject*)
{
}

bool IconObject::match(const std::string& namePattern,const std::string& typePattern)
{
	std::string s;
	bool nameOk=false;
	bool typeOk=false;
	
	if(namePattern.empty())
	{
		nameOk=true;
	}	
	else
	{  
		s=name_;
		std::transform(s.begin(), s.end(), s.begin(), ::tolower);
		if(s.find(namePattern) != string::npos)
  			nameOk=true;
	}
	
	if(typePattern.empty())
	{
	  	typeOk=true;
	}
	else
	{	  	
		s=class_.name();
		std::transform(s.begin(), s.end(), s.begin(), ::tolower);
		if(s.find(typePattern) != string::npos)
  			typeOk=true;
		if(!typeOk)
		{	
		  	s=class_.type();
			std::transform(s.begin(), s.end(), s.begin(), ::tolower);
			if(s.find(typePattern) != string::npos)
  				typeOk=true;
		}
		if(!typeOk)
		{	
			s=class_.iconBox();
			std::transform(s.begin(), s.end(), s.begin(), ::tolower);
			if(s.find(typePattern) != string::npos)
  				typeOk=true;
		}	
	}
	
	
	return nameOk && typeOk;
}	

bool IconObject::checkRequest()
{
	Request r=request();	
	if(r)
	{  
		string verb=r.getVerb();
		string icName=iconClass().name();
				
		//We convert the string to uppercase for comparison
		std::transform(verb.begin(), verb.end(), verb.begin(), ::toupper);	
		std::transform(icName.begin(), icName.end(), icName.begin(), ::toupper);		
				
		return (verb == icName);
	}	
	return false;	       
}

