#include <tinyxml.h>
#include <iostream>
#include <locale>
#include <sstream>
#include <fstream>
#include <stdint.h>
#include <vector>
#include <map>
#include <algorithm>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>

using namespace std;
int modNum=38;
string prepath="/home/dur/work/online/gppd/sim_online/instrument/";

void genCalFile(string bankName, std::vector<string> moduleGroup, string modinfo[][9])
{
    cout<<"genCalFile started!"<<endl;
    string cname=prepath+"/instrument/GPPD/"+bankName+".cal";
    ofstream calf;
    calf.open(cname.c_str(), std::ofstream::out);
    std::vector<string>::iterator it;
    int num_mod=moduleGroup.size();
    string tmpstr;
    int index;
    int group_tmp=atoi(bankName.substr(5,1).c_str());
    int sta=0;
    calf<<"# Calibration file for instrument GPPD CSNS edited on 2017-03-29."<<endl;
    calf<<"# Format: number    UDET         offset    select    group"<<endl;
    for(it=moduleGroup.begin();it!=moduleGroup.end();it++)
    {
        string name_mod=*it;
        index=atoi(name_mod.substr(7,1).c_str());
        tmpstr=modinfo[index-1][3];
        int id_tmp=atoi(tmpstr.c_str());
        tmpstr=modinfo[index-1][4];
        int xpid=atoi(tmpstr.c_str());
        tmpstr=modinfo[index-1][5];
        int ypid=atoi(tmpstr.c_str());
        int tpid=xpid*ypid;
        for(int q=0; q!=tpid;q++){
            calf<<sta<<" "<<(q+id_tmp)<<" "<<"0"<<" "<<"1"<<" "<<group_tmp<<endl;
            sta++;
            //cout<<sta<<endl;
        }
    }
    calf.close();
    cout<<"genCalFile finished!"<<endl;
}

std::vector<string> getModuleList(string detectorfile)
{
    //std::multimap<string,string> bankInfo;
    std::vector<string> modList;
    std::ifstream bankfile(detectorfile.c_str());
    //string bankbuff;
    //string bname;
    string mlist;
    for(int i =0; i!=modNum; i++){
        //getline(bankfile,bankbuff);
        getline(bankfile, mlist);        
        //std::vector<string> substring;
        //boost::split(substring, bankbuff, boost::is_any_of(";"), boost::token_compress_on);
        //bname=substring[0].c_str();
        //mlist=substring[1].c_str();
        //mlist=substring[0].c_str();
        //bankInfo.insert(std::multimap<string, string>::value_type(bname, mlist));
        modList.push_back(mlist);
    }
    return modList;
}

int main(){

    string modinfo[modNum][10];
    string module_file=prepath+"rawData/module.txt";
    cout<<module_file<<endl;
    std::ifstream modfile(module_file.c_str());
    std::string modbuff;
    for(int i = 0 ; i!=modNum; i++){
        getline(modfile,modbuff);
        std::vector<string> substring;
        boost::split(substring, modbuff, boost::is_any_of(";"), boost::token_compress_on);
        for(int j =0; j!=10; j++){
            cout<<substring[j]<<endl;
            modinfo[i][j]=(substring[j]);
        }
    }
    cout<<"get module infomation"<<endl;
    // test module file information
    //for(int i=0;i!=modNum;i++){
    //cout<<modinfo[i][0]<<" ; "<<modinfo[i][1]<<" ; "<<modinfo[i][2]<<" ; "<<modinfo[i][3]<<" ; "<<modinfo[i][4]<<" ; "<<modinfo[i][5]<<" ; "<<modinfo[i][6]<<" ; "<<modinfo[i][7]<<" ; "<<modinfo[i][8]<<" ; "<<modinfo[i][9]<<endl;
    //}
    string detectorfile=prepath+"rawData/detector.txt";
    //std::multimap<string, string> bankInfo=getBankInfo(detectorfile); 
    std::vector<string> moduleGroup=getModuleList(detectorfile); 
    std::vector<string>::iterator it;
    //std::vector<string> bankGroup;
    //std::vector<string> moduleGroup;
    //string bankName;
    //for(it=bankInfo.begin(); it!=bankInfo.end();it++)
    //{
        //bankGroup.push_back(it->first);
    //}
    //test bank list information
    //cout<<"the size of banklist: "<<bankInfo.size()<<endl;
    //for(it=bankInfo.begin();it!=bankInfo.end();it++)
    //cout<<it->first<<" "<<it->second<<endl;

    //arrange all banks in bankGroup, erase repeated bank name
    //std::vector<string>::iterator it2;
    //std::vector<string>::iterator it3;
    //it2=unique(bankGroup.begin(), bankGroup.end());
    //bankGroup.erase(it2,bankGroup.end());
    // test bank group information
    //for(it2=bankGroup.begin();it2!=bankGroup.end();it2++)
    //cout<<*it2<<endl;

    TiXmlElement *root, *tree, *node, *group, *son;
    // iterator banks
    //for(it2=bankGroup.begin();it2!=bankGroup.end();it2++){
    for(it=moduleGroup.begin();it!=moduleGroup.end();it++){
        string moduleName=*it;
        //it=bankInfo.find(bankName);
        //for(int i=0; i!=bankInfo.count(bankName); i++){
            //moduleGroup.push_back(it->second);
            //it++;}
        cout<<"create moduleGroup!"<<endl;
        //=======================create xml==========================================================
        TiXmlDocument doc;
        TiXmlDeclaration *delc =new TiXmlDeclaration("1.0", "utf-8", "");
        doc.LinkEndChild(delc);   

        root=new TiXmlElement("instrument");    
        root->SetAttribute("xmlns","http://www.mantidproject.org/IDF/1.0");
        root->SetAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
        root->SetAttribute("xsi:schemaLocation","http://www.mantidproject.org/IDF/1.0 Schema/IDFSchema.xsd");
        root->SetAttribute("name","GPPD");
        root->SetAttribute("valid-from","2017-01-29 00:00:00");
        root->SetAttribute("valid-to","2050-01-29 00:00:00");
        root->SetAttribute("last-modified","2017-06-29 00:00:00");
        doc.LinkEndChild(root);
        //==========================set defaults====================================================

        tree=new TiXmlElement("defaults");
        root->LinkEndChild(tree);

        node=new TiXmlElement("length");
        node->SetAttribute("unit","meter");
        tree->LinkEndChild(node);
        node=new TiXmlElement("angle");
        node->SetAttribute("unit","degree");
        tree->LinkEndChild(node);

        node=new TiXmlElement("reference-frame");
        tree->LinkEndChild(node);

        group=new TiXmlElement("along-beam");
        group->SetAttribute("axis", "z");
        node->LinkEndChild(group);
        group=new TiXmlElement("pointing-up");
        group->SetAttribute("axis", "y");
        node->LinkEndChild(group);
        group=new TiXmlElement("handedness");
        group->SetAttribute("val","right");
        node->LinkEndChild(group);

        //============================set moderator=================================================
        tree=new TiXmlElement("component");
        tree->SetAttribute("type","moderator");
        root->LinkEndChild(tree);

        node=new TiXmlElement("location");
        node->SetAttribute("z","-30.0");
        tree->LinkEndChild(node);

        tree=new TiXmlElement("type");
        tree->SetAttribute("is","Source");
        tree->SetAttribute("name","moderator");
        root->LinkEndChild(tree);
        //============================set sample===================================================
        tree=new TiXmlElement("component");
        tree->SetAttribute("type","sample-position");
        root->LinkEndChild(tree);

        node=new TiXmlElement("location");
        node->SetAttribute("x","0.0");
        node->SetAttribute("y","0.0");
        node->SetAttribute("z","0.0");
        tree->LinkEndChild(node);

        tree=new TiXmlElement("type");
        tree->SetAttribute("is","SamplePos");
        tree->SetAttribute("name","sample-position");
        root->LinkEndChild(tree);


        //============================set detector===============================================
        const char *p=moduleName.c_str();
        tree=new TiXmlElement("component");
        tree->SetAttribute("type",p);
        root->LinkEndChild(tree);
        node=new TiXmlElement("location");
        tree->LinkEndChild(node);

        //============================set module information=====================================
        tree=new TiXmlElement("type");
        tree->SetAttribute("name",p);
        root->LinkEndChild(tree);

        int index=0;
        for(int i=0; i<modNum; i++)
        {
            if (modinfo[i][0]==moduleName)
            {

                node=new TiXmlElement("component");
                node->SetAttribute("type","panel");
                node->SetAttribute("idfillbyfirst","x");

                p=(modinfo[i][4]).c_str();
                node->SetAttribute("idstart", p);    

                p=(modinfo[i][5]).c_str();
                node->SetAttribute("idstepbyrow", p);
                tree->LinkEndChild(node);

                group=new TiXmlElement("location");
                group->SetAttribute("name", p);

                p=(modinfo[i][1]).c_str();
                group->SetAttribute("x", p);
                p=(modinfo[i][2]).c_str();
                group->SetAttribute("y", p);
                p=(modinfo[i][3]).c_str();
                group->SetAttribute("z", p);
                node->LinkEndChild(group);

                son=new TiXmlElement("rot");
                p=(modinfo[i][9]).c_str();
                son->SetAttribute("val", p);
                son->SetAttribute("axis-x", "0.0");
                son->SetAttribute("axis-y", "1.0");
                son->SetAttribute("axis-z", "0.0");
                group->LinkEndChild(son);
                break;
            }else
                continue;
        }
            //==========================set rectangular detector====================================
            tree=new TiXmlElement("type");
            tree->SetAttribute("is","rectangular_detector");
            tree->SetAttribute("name","panel");
            tree->SetAttribute("type","pixel");

            p=(modinfo[0][5]).c_str();
            tree->SetAttribute("xpixels", p);

            p=(modinfo[0][7]).c_str();
            tree->SetAttribute("xstep", p);
            float xstep=atof(p);
            float result=0-xstep/2;
            ostringstream oss;
            oss<<result;
            string str(oss.str());
            oss.str("");
            tree->SetAttribute("xstart", str.c_str());

            p=(modinfo[0][6]).c_str();
            tree->SetAttribute("ypixels", p);
            p=(modinfo[0][8]).c_str();
            tree->SetAttribute("ystep", p);
            float ystep=atof(p);
            result=0-ystep/2;
            oss<<result;
            str=oss.str();
            oss.str("");
            tree->SetAttribute("ystart", str.c_str());

            root->LinkEndChild(tree);

            node=new TiXmlElement("properties");
            tree->LinkEndChild(node);

            //==========================set pixel size=============================================
            tree=new TiXmlElement("type");
            tree->SetAttribute("is","detector");
            tree->SetAttribute("name","pixel");
            root->LinkEndChild(tree);

            node=new TiXmlElement("cuboid");
            node->SetAttribute("id", "pixel-shape");
            tree->LinkEndChild(node);

            string pshape[4][3];
            result=0-xstep/2;
            oss<<result;
            str=oss.str();
            oss.str("");
            pshape[0][0]=str;
            pshape[1][0]=str;
            pshape[2][0]=str;
            result=xstep/2;
            oss<<result;
            str=oss.str();
            oss.str("");
            pshape[3][0]=str;

            result=0-ystep/2;
            oss<<result;
            str=oss.str();
            oss.str("");
            pshape[0][1]=str;
            pshape[2][1]=str;
            pshape[3][1]=str;

            result=ystep/2;
            oss<<result;
            str=oss.str();
            oss.str("");
            pshape[1][1]=str;

            pshape[0][2]="0.0";
            pshape[1][2]="0.0";
            pshape[2][2]="-0.0001";
            pshape[3][2]="0.0";

            group=new TiXmlElement("left-front-bottom-point");
            group->SetAttribute("x",pshape[0][0].c_str());    
            group->SetAttribute("y",pshape[0][1].c_str());    
            group->SetAttribute("z",pshape[0][2].c_str());    
            node->LinkEndChild(group);

            group=new TiXmlElement("left-front-top-point");
            group->SetAttribute("x",pshape[1][0].c_str());    
            group->SetAttribute("y",pshape[1][1].c_str());    
            group->SetAttribute("z",pshape[1][2].c_str());    
            node->LinkEndChild(group);

            group=new TiXmlElement("left-back-bottom-point");
            group->SetAttribute("x",pshape[2][0].c_str());    
            group->SetAttribute("y",pshape[2][1].c_str());    
            group->SetAttribute("z",pshape[2][2].c_str());    
            node->LinkEndChild(group);

            group=new TiXmlElement("right-front-bottom-point");
            group->SetAttribute("x",pshape[3][0].c_str());    
            group->SetAttribute("y",pshape[3][1].c_str());    
            group->SetAttribute("z",pshape[3][2].c_str());    
            node->LinkEndChild(group);

            node=new TiXmlElement("algebra");
            node->SetAttribute("val","pixel-shape");
            tree->LinkEndChild(node);

            cout<<"create xml structure"<<endl;
            //=================== write xml and cal file============================================
            string name=prepath+"idfData/"+moduleName+".xml";
            cout<<"idf name: "<<name<<endl;
            doc.SaveFile(name.c_str());
            cout<<"save xml file"<<endl;
            // write cal file
            //genCalFile(bankName, moduleGroup, modinfo);
            //moduleGroup.erase(moduleGroup.begin(),moduleGroup.end());
        }
        //moduleGroup.clear();
        return 1;
    }
