#include <amscudafractallevelset/amscudafractallevelset.cuh>
#include <amsmathutil25/amsmathutil25.hpp>
#include <amsstring4/amsstring4.hpp>

using namespace ams;

namespace amscuda
{
namespace fractallevelset
{

    static const int omode_stloutput = 1;
    static const int omode_counttriangles = 2;
    static const int omode_countarea = 3;
    
    

    inputpars::inputpars()
    {
        xyz_min = cuvec3f(-1.2,-1.2,-1.2);
        xyz_max = cuvec3f(1.2,1.2,1.2);

        Nx = 32;
        Ny = 32;
        Nz = 32;

        nblockdiv = 32;
        nthreads = 256;

        isjulia = 0;
        juliac = cuvec3f(0,0,0);
        exponent = 8.0f;

        operatingmode = omode_stloutput;
        verbosity = 0; //0 silent, 1


    }

    

    int load_inputfile(const char *fname, amsarray<amsarray<amsstring>> *stringpars)
    {
        int ret = 1;
        int res;
        amsstring ftxt,ln;
        amsarray<amsstring> lns;
        amsarray<amsstring> pair;
        int I;

        if(stringpars==NULL)
        {
            printf("parse_input error: stringpars is NULL\n");
            return -1;
        }

        stringpars->resize(0);

        res = readtextfile(fname,&ftxt);
        if(res<0)
        {
            printf("parse_input error: could not open %s for reading.\n",fname);
            return -1;
        }

        lns = ftxt.splitlines();
        for(I=0;I<lns.length;I++)
        {
            ln = lns[I].splitfirst("#")[0];
            ln = ln.splitfirst("//")[0];
            if(ln.length>0)
            {
                pair = ln.splitfirst("=");
                if(pair[0].length>0 && pair[1].length>0)
                {
                    pair[0] = pair[0].stripwhitespace();
                    pair[1] = pair[1].stripwhitespace();
                    stringpars->append(pair);
                }
            }
        }

        // printf("debug: parfile pars:\n");
        // for(I=0;I<stringpars->length;I++)
        // {
        //     printf("%d:\t'%s'\t'%s'\n",I,stringpars->at(I)[0].cstring,stringpars->at(I)[1].cstring);
        // }

        return ret;
    }

    cuvec3f parse_as_cuvec3f(amsstring s)
    {
        int I;
        cuvec3f ret;
        amsarray<amsstring> alphanums;
        
        alphanums = s.splitalphanum();
        // printf("debug: alphanum[0]='%s'\n",alphanums[0].cstring);
        // printf("debug: alphanum[1]='%s'\n",alphanums[1].cstring);
        // printf("debug: alphanum[2]='%s'\n",alphanums[2].cstring);
        
        for(I=0;I<alphanums.length&&I<3;I++)
        {
            ret[I] = (float)(alphanums[I].strtonum());
        }

        return ret;
    }

    void parsearch_cuvec3f(amsstring varname, amsarray<amsarray<amsstring>> *stringpars, cuvec3f *val)
    {
        int I;
        for(I=0;I<stringpars->length;I++)
        {
            if(varname==stringpars->at(I)[0])
            {
                *val = parse_as_cuvec3f(stringpars->at(I)[1]);
            }
        }
        return;
    }

    void parsearch_float(amsstring varname, amsarray<amsarray<amsstring>> *stringpars, float *val)
    {
        int I;
        for(I=0;I<stringpars->length;I++)
        {
            if(varname==stringpars->at(I)[0])
            {
                // printf("debug: par[0]='%s'\n",stringpars->at(I)[0].cstring);
                // printf("debug: par[1]='%s'\n",stringpars->at(I)[1].cstring);
                *val = (float) stringpars->at(I)[1].strtonum();
            }
        }
        return;
    }

    void parsearch_int(amsstring varname, amsarray<amsarray<amsstring>> *stringpars, int *val)
    {
        int I;
        for(I=0;I<stringpars->length;I++)
        {
            if(varname==stringpars->at(I)[0])
            {
                *val = (int) stringpars->at(I)[1].strtonum();
            }
        }
        return;
    }

    void parsearch_bool(amsstring varname, amsarray<amsarray<amsstring>> *stringpars, bool *val)
    {
        int I;
        for(I=0;I<stringpars->length;I++)
        {
            if(varname==stringpars->at(I)[0])
            {
                *val = (bool) stringpars->at(I)[1].strtonum();
            }
        }
        return;
    }

    int parse_input(const char *fname, inputpars *pars)
    {
        int ret = 1;
        int res;
        amsarray<amsarray<amsstring>> stringpars;
        res = load_inputfile(fname,&stringpars);

        parsearch_cuvec3f("xyz_min",&stringpars,&(pars->xyz_min));
        parsearch_cuvec3f("xyz_max",&stringpars,&(pars->xyz_max));
        parsearch_int("Nx",&stringpars,&(pars->Nx));
        parsearch_int("Ny",&stringpars,&(pars->Ny));
        parsearch_int("Nz",&stringpars,&(pars->Nz));
        parsearch_int("nblockdiv",&stringpars,&(pars->nblockdiv));
        parsearch_int("nthreads",&stringpars,&(pars->nthreads));
        parsearch_int("operatingmode",&stringpars,&(pars->operatingmode));
        parsearch_int("verbosity",&stringpars,&(pars->verbosity));

        parsearch_bool("isjulia",&stringpars,&(pars->isjulia));
        parsearch_cuvec3f("juliac",&stringpars,&(pars->juliac));
        parsearch_float("exponent",&stringpars,&(pars->exponent));
        parsearch_int("fractaliter",&stringpars,&(pars->fractaliter));
        parsearch_float("threshold",&stringpars,&(pars->threshold));
        parsearch_float("freezethresh",&stringpars,&(pars->freezethresh));
        parsearch_float("scale",&stringpars,&(pars->scale));



        return ret;
    }

    void inputmain(int argc, char *argv[])
    {
        int res;
        amsstring fnameout;
        inputpars pars;
        marchingtet_pars mtpars;
        main_functionpars mfpars;
        bagoftriangles mesh;

        if(argc<3)
        {
            fnameout = "./output.stl";
        }
        else
        {
            fnameout = argv[2];
        }

        if(argc<2)
        {
            printf("useage: (execname) [inputfile.inp] [optional outputfile.stl]\n");
            return;
        }
        else
        {
            res = parse_input(argv[1],&pars);
            if(res<0) return;
        }

        #ifdef AMSCLS_MANDELBROT
            mfpars.isjulia = pars.isjulia;
            mfpars.juliac = pars.juliac;
            mfpars.exponent = pars.exponent;
            mfpars.fractaliter = pars.fractaliter;
            mfpars.threshold = pars.threshold;
            mfpars.freezethresh = pars.freezethresh;
        #endif

        #ifdef AMSCLS_PPKFRACTAL
            mfpars.isjulia = pars.isjulia;
            mfpars.juliac = pars.juliac;
            mfpars.fractaliter = pars.fractaliter;
            mfpars.threshold = pars.threshold;
            mfpars.freezethresh = pars.freezethresh;
            mfpars.scale = pars.scale;
        #endif

        //Fill operatingstructs
        mtpars.xyz_min = pars.xyz_min;
        mtpars.xyz_max = pars.xyz_max;
        mtpars.Nx = pars.Nx;
        mtpars.Ny = pars.Ny;
        mtpars.Nz = pars.Nz;
        mtpars.pars = mfpars;

        //execute model based on operatingmode
        if(pars.verbosity>=1)
            pars.print();
        if(pars.operatingmode==omode_stloutput)
        {
            marchingtets_gpu(mtpars,&mesh,pars.nblockdiv,pars.nthreads,pars.verbosity);
            mesh.save_stl(fnameout.cstring);
        }
        else if(pars.operatingmode==omode_counttriangles)
        {
            int64_t tricount = 0;
            tricount = marchingtets_tricount_gpu(mtpars,pars.nblockdiv,pars.nthreads,pars.verbosity);
            printf("triangle count = %ld\n",(long)tricount);
        }
        else if(pars.operatingmode==omode_countarea)
        {
            float area = 0.0f;
            area = marchingtets_area_gpu(mtpars,pars.nblockdiv,pars.nthreads,pars.verbosity);
            printf("area = %1.5g\n",area);
        }


    }

    __host__ void inputpars::print()
    {
        printf("inputpars:\n");
        {
            printf("\t operatingmode = %d \n",operatingmode);
            printf("\t verbosity = %d\n",verbosity);
            printf("\t xyz_min = (%1.3g,%1.3g,%1.3g)\n",xyz_min[0],xyz_min[1],xyz_min[2]);
            printf("\t xyz_max = (%1.3g,%1.3g,%1.3g)\n",xyz_max[0],xyz_max[1],xyz_max[2]);
            printf("\t Nx = %d\n",Nx);
            printf("\t Ny = %d\n",Ny);
            printf("\t Nz = %d\n",Nz);
            printf("\t nblockdiv = %d\n",nblockdiv);
            printf("\t nthreads = %d\n",nthreads);
            printf("\t isjulia = %d\n",(int)isjulia);
            printf("\t juliac = (%1.3g,%1.3g,%1.3g)\n",juliac[0],juliac[1],juliac[2]);
            printf("\t exponent = %1.3f\n",exponent);
            printf("\t fractaliter = %d\n",fractaliter);
            printf("\t threshold = %1.3f\n",threshold);
            printf("\t freezethresh = %1.3f\n",freezethresh);
            printf("\t scale = %1.3f\n",scale);
            


        }
    }


    
};//end namespace fractallevelset
};// end namespace amscuda
