//This support library has been made by Salvatore Russo and is released under GNU public license.
//It uses standard template libraries to create String class and StringList class used in DeleD
//plugins made by me.
//It also includes some facilities to add or retrieve header, or convert a long, integer or unsigned to a string,
//and get materials as a stringlist or handle callback errors.

#ifndef _SUPPORT_H_
#define _SUPPORT_H_

    //standard classes to use
#include <cstdio>
#include <string>
#include <deque>

    //so you know error types
#include "dll.h"

typedef std::string stdstring;

    //Define a class String based on STL string
class String : public stdstring {
    public:
            //constructors
        String() : stdstring() {};
        String(const char* str) : stdstring(str) {};
        String(const stdstring& str) : stdstring(str) {};
                //trim function in my implementation removes spaces but also \t and \n chars
                //from end and beginning of string only
        void Trim(){
          int k = (this->length())-1;
          while(k>=0){
            if((*this)[k]==' ' || (*this)[k]=='\t' || (*this)[k]=='\n'){
                this->erase(k,1);//trim    
                k=k-1;
            }
            else k=-1;    
          }
          k=0;
          while(k < this->length()){
            if((*this)[k]==' ' || (*this)[k]=='\t' || (*this)[k]=='\n'){
                this->erase(k,1);//trim    
            }
            else k=this->length();    
          }          
        }
                //replace all instaces of "from" char with "to" char
        void Replace(char from,char to){
        int k = (this->length())-1;
        while(k>=0){
        if((*this)[k]==from) (*this)[k]=to;//replace    
        k=k-1;
        }    
        } 
};    


using std::deque;

    //Define a class StringList based on STL deque and the defined class String
class StringList : public deque <String> {
    public:
        //Adds a String to StringList
    void Add(String str){push_back(str);}
        
        //Loads a stringlist from a file
        //note that each String added to StringList
        //is separed by a \n character and it's present
        //at the end of line.
    void LoadFromFile(const char* filename){
        FILE *nuovofile;
        nuovofile=fopen(filename,"r");//opens new file
        int c;
        String str;
        c = fgetc(nuovofile);
        while(c!=EOF){
            if(c!='\n'){
                str.append((char*)&c);
                }
            else {
                str.append((char*)&c);
                str.Trim();
                Add(str);
                str.clear();
                };
            c = fgetc (nuovofile);
        
            
        }
    }
        //GetText returns a String containing all the text included in
        //the StringList
    String GetText(){
        String str;
        for(int i=0;i<this->size();i++){
            str=str+(*this)[i];
        }   
        return str;
    }   

};

    //convert long integer to a string
String toString(long x) {
  char buffer[256];
  sprintf(buffer,"%ld",x);
  return String(buffer);
}

String toString(int x) {
  char buffer[256];
  sprintf(buffer,"%d",x);
  return String(buffer);
}

String toString(unsigned x) {
  char buffer[256];
  sprintf(buffer,"%u",x);
  return String(buffer);
}
    //This function subdivides a string in a list of strings
StringList SubdivideString(String str,String divider){
    StringList strings;//returned StringList
    strings.clear();//clear returned stringlist
    
    int c=0;
    int l=str.length();
    
    //process entire string
    while(c<l){
        String resultstr;
        resultstr.clear();
        //read characters until divider is encountered
        while((str[c]!=divider[0]) && c<l){
            resultstr=resultstr + str[c];
            c++;
        }    
        //Remove spaces \t and \n from string in my implementation...
        //pay attention or change it in dll.h if you don't want to remove
        //a particular char.
        resultstr.Trim();//trims string resultstr
        strings.Add(resultstr);//add trimmed string
        c++;
    }    
return strings;
}

    // adds the length of a string in bytes to that string,
    // including the length of the 'length' string itself!
String AddLengthToHeader(String header){
    long len=header.length()+(toString(header.length())).length()+1;
    String tempStr = toString(len)+String(";")+header;
    if (tempStr.length() > len) len++;
    return (toString(len)+String(";")+header);
}    

    // this function reads the header length and then extract
    // the whole header from data...
StringList GetHeader(String data,String delimeter){
    int position = 0;
    String myStr;
    myStr.clear();
    while (data[position] != delimeter[0]){      // headerlength is the first value of data
        myStr = myStr + data[position];
        position++;
    }    
    int headerlength = atoi(myStr.c_str());
    myStr.clear();
    myStr = data.substr(0,headerlength);            // retrieve complete header from data
    return SubdivideString(myStr,delimeter);      // return as stringlist

}

    //this function show an error message when giving callback error code
void ErrorMessage(unsigned error){
    String errore;
    errore.clear();
    errore.append("Error Code ");
    errore=errore+toString(error)+String(": ");
    switch(error){
        case PE_NODATA :
            errore.append("No Data present.");
            break;
        case PE_INVALIDVERSION :
            errore.append("Invalid Version.");
            break;
        case PE_INVALIDMEM :
            errore.append("Invalid GetMem Command.");
            break;
        case PE_INVALIDGET :
            errore.append("Invalid GetData Command.");
            break;
        case PE_INVALIDSET:
            errore.append("Invalid SetData Command.");
            break;
        case PE_INVALIDHEADER:
            errore.append("Invalid Header.");
            break;
        case PE_INVALIDDATASTRING:
            errore.append("Invalid Data String.");
            break;
        case PE_INVALIDDATAPROPERTY:
            errore.append("Invalid Data Property.");
            break;
        case PE_OBJECTDATA:
            errore.append("Error in Object Data.");
            break;
        case PE_LIGHTDATA:
            errore.append("Error in Light Data.");
            break;
        case PE_MATERIALDATA:
            errore.append("Error in Material Data.");
            break;
        case PE_UNKNOWNREASON:
            errore.append("Unknown Callback Reason.");
            break;
        case PE_UNKNOWNERROR:
            errore.append("Unknown Error.");
            break;
        default:
            errore.append("Unknown Error Code.");
            break;
    }
    MessageBox(NULL,errore.c_str(),"Error in Callback",MB_ICONERROR);    
    
}        
    //This function returns a string made of all data
    //to write materials on a DMF file.
    //it includes ";" and as first element the number of materials
String  GetMaterials( String materials_data, int num_materials ){
    StringList materiali;//A stringlist with all materials data
    StringList temp_list=SubdivideString(materials_data,String(";"));//data from DeleD
    String temp=toString(num_materials)+String(";");
    materiali.Add(temp);//Add number of materials
    temp.clear();
    int offs=0;
    //Add materials
    for( int i=0;i<num_materials;i++){
         int n_layers=atoi(temp_list[offs+4].c_str());
         String curr_mat;
         for(int k=offs;k<=offs+4+n_layers;k++) curr_mat=curr_mat+temp_list[k]+String(";");
         materiali.Add(curr_mat);
         offs+=4+n_layers+1;
         }
    String ret;//return string
    for(int i=0;i<materiali.size();i++) ret+=materiali[i]+String("\n");
    return ret;//return materials
}

    //This function returns a string made of all data
    //to write objects on a DMF file.
    //it includes ";" and as first element the number of objects
String GetObjects( String objects_data, int num_objects){
       //we'll extract only data from DeleD string and copy them into a StringList
       StringList Objects=SubdivideString(objects_data,String(";"));
       StringList temp_objs;
       temp_objs.Add(toString(num_objects)+String(";"));                 
       int index=0;
       for(int obj=0;obj<num_objects;obj++){
       //(Object information: name,ID,type,visible,snapmethod,groupid,autoUV,tag)
       temp_objs.Add( Objects[index]+String(";")+
                      Objects[index+1]+String(";")+
                      Objects[index+2]+String(";")+
                      Objects[index+3]+String(";")+
                      Objects[index+4]+String(";")+
                      Objects[index+5]+String(";")+
                      Objects[index+6]+String(";")+
                      Objects[index+7]+String(";"));
       index=index+8;
       int num_vertices=atoi(Objects[index].c_str());//(Number of vertices in this object)
       temp_objs.Add( Objects[index]+String(";") );
       index++;
       for(int vert=0;vert<num_vertices;vert++){
       //(Vertice information: X,Y,Z values)
       temp_objs.Add( Objects[index]+String(";")+
                      Objects[index+1]+String(";")+
                      Objects[index+2]+String(";"));
       index=index+3;    
       }    
       int num_polygons=atoi(Objects[index].c_str());
       //(Number of polygons in this object)
       temp_objs.Add( Objects[index]+String(";") );
       index++;
       for(int poly=0;poly<num_polygons;poly++){
               int num_items = (atoi(Objects[index].c_str())*3)+2;//(Number of data to write for each poly)
                                String temp_str;
                                for(int item=0;item<num_items;item++){
                                    if(item<num_items-1) temp_str+=Objects[index+item]+String(";");
                                    else temp_objs.Add( temp_str+Objects[index+item]+String(";") );
                                }
                                temp_str.clear();
                                index=index+num_items;        
                            }
}    
String ret;//return string
for(int i=0;i<temp_objs.size();i++) ret+=temp_objs[i]+String("\n");
return ret;
}

    //This function returns a string made of all data
    //needed to import a basic cube in DeleD.
String CreateDeleDCube(){
    String MyChars;
       //create a cube object to pass to DeleD
    MyChars.append("Cube0;0;1;-1;1;-1;-1;User info;");//Object information: name,ID,type,visible,snapmethod,groupid,autoUV,tag
    MyChars.append("8;");//Number of vertices in this object
    MyChars.append("128;0;128;");//Vertice information: X,Y,Z values
    MyChars.append("-128;0;128;");
    MyChars.append("-128;0;-128;");
    MyChars.append("128;0;-128;");
    MyChars.append("128;128;128;");
    MyChars.append("-128;128;128;");
    MyChars.append("-128;128;-128;");
    MyChars.append("128;128;-128;");
    MyChars.append("6;");//Number of polygons in this object
    MyChars.append("4;0;0;1;5;4;1.0000;0.0000;-1.0000;0.0000;-1.0000;1.0000;1.0000;1.0000;");//Polygon information: # of vertices,textureID,vertice index values,uv values (2 per vertice)
    MyChars.append("4;0;1;2;6;5;1.0000;0.0000;-1.0000;0.0000;-1.0000;1.0000;1.0000;1.0000;");
    MyChars.append("4;0;2;3;7;6;-1.0000;0.0000;1.0000;0.0000;1.0000;1.0000;-1.0000;1.0000;");
    MyChars.append("4;0;3;0;4;7;-1.0000;0.0000;1.0000;0.0000;1.0000;1.0000;-1.0000;1.0000;");
    MyChars.append("4;0;3;2;1;0;1.0000;-1.0000;-1.0000;-1.0000;-1.0000;1.0000;1.0000;1.0000;");
    MyChars.append("4;0;4;5;6;7;1.0000;1.0000;-1.0000;1.0000;-1.0000;-1.0000;1.0000;-1.0000;");
    return MyChars;
}       

#endif /* _SUPPORT_H_ */
