/* ImLib3D
 * Copyright (c) 2001, ULP-IPB Strasbourg.
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*! \file ImageProcessorsDescription.hpp */

#ifndef _ImageProcessorsDescription_hpp
#define _ImageProcessorsDescription_hpp

#include<ImLib3D/XMLImageProcessorsDescription.hpp>

class ImProcNode;
//! Callback for walking through the image processor tree 
class ImProcTreeCallBack
{
public:
	//! if returns false, traversal stops
	virtual bool ImProcNodeCallBack(ImProcNode *node,int depth)=0;
	virtual ~ImProcTreeCallBack(){;}
};
//! Image processor node, in it's hierarichical classification tree
class ImProcNode
{
public:
	string name;
	ImProcNode *parent;
	vector<ImProcNode *> subnodes;
	
	void Walk(ImProcTreeCallBack *cb,int depth=0)
	{
		if(!cb->ImProcNodeCallBack(this,depth)){return;}
		for(uint i=0;i<subnodes.size();i++){subnodes[i]->Walk(cb,depth+1);}
	}
	ImProcNode *FindNode(const string &sname)
	{
		if(name==sname){return this;}
		for(uint i=0;i<subnodes.size();i++)
		{
			ImProcNode *res=subnodes[i]->FindNode(sname);
			if(res){return res;}
		}
		return NULL;
	}
	ImProcNode(string _name):name(_name),parent(NULL){;}
};



//! Hierarichical classification tree of image processors
class ImProcTree : public ImProcTreeCallBack
{
public:
	enum WalkType {WalkShow,WalkMaxDepth,WalkSetParents}; 
	WalkType walkType;
	int maxDepth;
	void Walk(ImProcTreeCallBack *cb){root.Walk(cb);}
	bool ImProcNodeCallBack(ImProcNode *node,int depth)
	{
		if(walkType==WalkSetParents)
		{
			for(uint i=0;i<node->subnodes.size();i++)
			{node->subnodes[i]->parent=node;}
		}
		if(walkType==WalkMaxDepth)
		{
			maxDepth=max(depth,maxDepth);
		}
		if(walkType==WalkShow)
		{
			for(int j=0;j<depth*4;j++){printf(" ");}
			printf("%s\n",node->name.c_str());
		}
		return true;
	}
	ImProcNode root;
	void Show(){walkType=WalkShow;root.Walk(this);}
	void SetParents(){walkType=WalkSetParents;root.Walk(this);}
	int Depth()
	{
		maxDepth=0;
		walkType=WalkMaxDepth;
		root.Walk(this);
		return maxDepth;
	}
	ImProcNode *FindNode(const string &sname)
	{
		return root.FindNode(sname);
	}
	ImProcTree():root("ImageProcessor")
	{
	}
};

//! This an interface to the xml image processor description file
/*! IMPORTANT image processors are classified in hierchical manner, but the XML tree
they are stored in does NOT reflect that hierarchy. The ImProcTree class can be used
if that hierarchy is needed.
*/
class ImageProcessorsDescription : public XIPD::ImageProcessorsDescription
{
protected:
	//! a hierachy tree of processors
	ImProcTree imProcTree;
	//! Inserts all nodes described by this description into tree
	void CreateTree();
public:
	ImProcTree &GetImProcTree(){return imProcTree;}
	void PrintTree(){imProcTree.Show();}

	template<class T>
	int FindName(const vector<T> &vect,const string &name)
	{
		for(uint i=0;i<vect.size();i++)
		{
			if(vect[i].name==name){return i;}
		}
		return -1;
	}
	//! descritpion of an argument type
 	XIPD::ArgumentType *GetArgumentType(const string &name,bool fail=true)
 	{
		int pos=FindName(argumentTypes,name);
		if(fail && pos<0){ThrowError("ImageProcessorsDescription::GetArgumentType could not find \"%s\"",name);}
		return pos>=0 ? &argumentTypes[pos] : NULL;
 	}
	//! descritpion of an argument type
	XIPD::TemplateGroup *GetTemplateGroup(const string &name,bool fail=true)
	{
		int pos=FindName(templateGroups,name);
		if(fail && pos<0){ThrowError("ImageProcessorsDescription::GetTemplateGroup could not find \"%s\"",name);}
		return pos>=0 ? &templateGroups[pos] : NULL;
	}
	//! an arbitrary image processor 
	XIPD::ImageProcessor *GetImageProcessor(const string &name,bool fail=true)
	{
		int pos=FindName(imageProcessors,name);
		if(fail && pos<0){ThrowError("ImageProcessorsDescription::GetImageProcessor could not find \"%s\"",name);}
		return pos>=0 ? &imageProcessors[pos] : NULL;
	}
public:
	//! Returns all image processors and nodes
	vector<string> GetNames(bool imProcsOnly=false)
	{
		vector<string> res;
		for(uint i=0;i<imageProcessors.size();i++)
		{
			res.push_back(imageProcessors[i].name);
		}
		if(!imProcsOnly)
		{
			for(uint i=0;i<imageProcessorNodes.size();i++)
			{
				res.push_back(imageProcessorNodes[i].name);
			}
		}
		return res;		
	}

public:
	//! transforms a set of strings so they all have the same width
	void Pad(vector<string> &s)
	{
		uint mxsize=0;
		for(uint i=0;i<s.size();i++){mxsize=max(mxsize,s[i].size());}		 
		for(uint i=0;i<s.size();i++){s[i].insert(s[i].size(), mxsize-s[i].size(), ' ');}	
	}
	//! Prints a (long) string, using word wrap and margin
	void Print(const string &s,uint margin,int width)
	{
		int linepos=0;
		string line="";
		for(uint i=0;i<s.size();i++)
		{
			line+=(s[i]!='\n' ? s[i] : ' ');
			// check for end of line
			if(linepos>=width || i==s.size()-1 || s[i]=='\n')
			{
				// if not end of string then wrap
				if(i!=s.size()-1)
				{
					uint j;
					for(j=i;j>0 && (s[j]!=' ' && s[j]!='\n');j--);//find previous ' '
					line=line.substr(0,line.size()-(i-j));// remove wraped word from line
					i=j;// reset for next line
				}				
				// output margin and line
				for(uint j=0;j<margin;j++){printf(" ");}
				printf("%s\n",line.c_str());
				linepos=0;
				line="";
			}
			linepos++;
		}
	}
public:
	//! creates output in a format needed for inserting auto class doc in source file
//	void PrintDoxyDoc(const string& name,const string& type="class head");
	//! prints full description of an image processor to std output
//	void PrintHelp(const string &name);
public:
	void Merge(XIPD::ImageProcessorsDescription &other);
	
public:
	//! read description from xml file
	void ReadFromString(const string &description)
	{
		XMLTree tmpTree;
		tmpTree.read_buffer(description);
		XIPD::ImageProcessorsDescription tmp(tmpTree.root());
		*(XIPD::ImageProcessorsDescription *)this=tmp;
	}
	//! read description from xml file
	void ReadFromFile(const string &fname)
	{
		XIPD::ImageProcessorsDescription tmp(XMLTree(fname).root());
		*(XIPD::ImageProcessorsDescription *)this=tmp;
	}
//  	{
//  		if(!tree.read(fname)){ThrowError("failed reading %s",fname);}
//  	}
	//! pos-construction initialisation
	virtual void Init(){CreateTree();}
	virtual ~ImageProcessorsDescription(){;}
	ImageProcessorsDescription(){;}
	ImageProcessorsDescription(const string& descr):
		XIPD::ImageProcessorsDescription(XMLTree(descr).root())
	{
//  		 tree;
//  		tree.read_buffer(descr);
//  		XIPD::ImageProcessorsDescription tmp(tree.root);
//		*this=tmp;
	}
};


#endif // _ImageProcessorsDescription_hpp
