/***************************** 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 IconObject_H
#include "IconObject.h"
#endif

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

#ifndef IconInfo_H
#include "IconInfo.h"
#endif

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


#ifndef State_H
#include "State.h"
#endif

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

#ifndef Editor_H
#include "Editor.h"
#endif

#ifndef Service_H
#include "Service.h"
#endif

#ifndef Command_H
#include "Command.h"
#endif

#ifndef Request_H
#include "Request.h"
#endif

#ifndef Queue_H
#include "Queue.h"
#endif

#ifndef Path_H
#include "Path.h"
#endif

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

#ifndef Tokenizer_H
#include "Tokenizer.h"
#endif

#ifndef Dependancy_H
#include "Dependancy.h"
#endif

#ifndef LogWindow_H
#include "LogWindow.h"
#endif

#ifndef Service_H
#include "Service.h"
#endif

#include <sstream>

#include "Metview.h"

#include <sys/stat.h>

IconObject::IconObject(Folder* parent,const IconClass& kind,const string& name,
		IconInfo* info):
	parent_(parent),
	class_(kind),
	name_(name),
	info_(info),
	queue_(0),
	log_(0),
	editor_(0),
	logWindow_(0),
	locked_(false),
	link_(false)
{
	if ( parent_ )  {
		parent_->attach();

		struct stat s;
		string p = path().str();
		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;
	}

}

IconObject::~IconObject()
{	  
	reset(); 
	if ( parent_ )  parent_->detach();
	delete info_;
}

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
{
	std::stringstream s;
	s << parent()->fullName() << "/" << name();
	return s.str();
}

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

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("InternalMetviewUI");

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

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

IconObject* IconObject::search(const string& fullName)
{
	//cout << "search " << fullName << endl;

	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_dot  = dotPath();
		Path old_emb  = embeddedPath();

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

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

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

		modified();
	}
}

bool IconObject::rename(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;
	}

	modified();

	string old_name = name_;

	Path old_path = path();
	Path old_dot  = dotPath();
	Folder* old_emb  = embeddedFolder(false);

	name_ = new_name;

	Path new_path = path();
	Path new_dot  = dotPath();

	old_dot.rename(new_dot);
	old_path.rename(new_path);
	if(old_emb)
	{
		cout << "Rename " << *old_emb << endl;
		cout << "Rename " << embeddedName(name()) << endl;

		old_emb->rename(embeddedName(name())); 
	}
	else cout << "Rename : no embedded folder " << endl;

	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()
{
	if(!dotPath().exists())
		saveInfo();

	if(info().fix(class_.name()))
		saveInfo();
}

set<string> IconObject::can()
{
	set<string> c = iconClass().can();
	if(log_ != 0) c.insert("output");
	return c;
}

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


void IconObject::edit()
{
	Editor::open(this);
}

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

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

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

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

IconObject* IconObject::clone(Folder* f, bool update)
{

	createFiles();

	string new_name;
	
	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_dot  = dotPath();
	Path old_emb  = embeddedPath();

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

	old_path.copy(new_path);
	old_dot.copy(new_dot);
	if(old_emb.exists())
		old_emb.copy(new_emb);

	return IconFactory::create(f,new_name);

}




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

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

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

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

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

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

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

void IconObject::removeFiles()
{
	modified();
	path().remove();
	dotPath().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_->close();

	delete log_;
	log_ = 0;

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

	dependancies_.clear();
}

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

void IconObject::notifyWaiting()
{
	parent()->tellObservers(&FolderObserver::waiting,this);
}

void IconObject::notifyError()
{
	parent()->tellObservers(&FolderObserver::error,this);	
}

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

void IconObject::notifyChanged()
{
	parent()->tellObservers(&FolderObserver::modified,this);	

	for(set<IconObserver*>::iterator j = observers_.begin(); j != observers_.end(); ++j)
		(*j)->changed(this);
}

void IconObject::notifyReady()
{
	parent()->tellObservers(&FolderObserver::ready,this);	
}
void IconObject::notifyOpened()
{
	parent()->tellObservers(&FolderObserver::opened,this);	
}
void IconObject::notifyClosed()
{
	parent()->tellObservers(&FolderObserver::closed,this);	
}



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

void IconObject::addObserver(IconObserver* o)
{
	observers_.insert(o);
}

void IconObject::removeObserver(IconObserver* o)
{
	observers_.erase(o);
}

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

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

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

void IconObject::destroy()
{
	IconObjectH save = this;
	removeFiles();
	parent()->release(this);
	notifyDestroyed();
	if(editor_) editor_->empty();
}

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

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

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)
		{
			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::subObjects(const string& name,
	const vector<IconObjectH>& sub,Request& r)
{
	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;
		}
	}

}

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::isInTimer() const
{
	return false;
}

void IconObject::drop(IconObject*)
{
}
