#include <Core/Arrays/ARRAY.h>
#include <Core/Parsing/PARSE_ARGS.h>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>

using namespace PhysBAM;
typedef double T;

ARRAY<std::string> patterns,inputs;
ARRAY<std::regex> rs;
ARRAY<int> cols;
ARRAY<int> resolutions;
T tolerance=0;

std::pair<bool,T> Match_Pattern(const std::string& line,int p)
{
    if(!std::regex_search(line,rs(p))) return {false,0};
    std::stringstream ss(line);
    std::string token;
    for(int i=0;i<cols(p);i++)
        if(!(ss>>token))
            return {false,0};
    const char* start=token.c_str()+token.find_first_of("+-0123456789.");
    if(!*start) return {false,0};
    return {true,strtod(start,0)};
}

ARRAY<T> Read_Input_File(const std::string& file)
{
    std::ifstream fin(file);
    if(!fin)
    {
        LOG::printf("Could not read file %P\n",file);
        exit(1);
    }
    std::string line,token;
    ARRAY<T> data(patterns.m);
    ARRAY<bool> found(patterns.m);
    while(getline(fin,line))
    {
        for(int p=0;p<patterns.m;p++)
        {
            auto [b,x]=Match_Pattern(line,p);
            if(b)
            {
                data(p)=x;
                found(p)=true;
            }
        }
    }
    for(int p=0;p<patterns.m;p++)
        if(!found(p))
            data(p)=std::nan("");
            //LOG::printf("Could not find pattern '%s'\n",patterns(p));
    return data;
}

T Compute_Order(T r0,T e0,T r1,T e1)
{
    return log(abs(e0)/abs(e1))/log(r1/r0);
}

int main(int argc,char *argv[])
{
    bool out_latex=false;
    PARSE_ARGS parse_args(argc,argv);
    parse_args.Add("-p",&patterns,"pattern","pattern identifying line with error value (may be repeated)");
    parse_args.Add("-f",&cols,"column-index","1-indexed token index of error value on line (may be repeated)");
    parse_args.Add("-r",&resolutions,"res","resolution of simulation (may be repeated)");
    parse_args.Add("-i",&inputs,"file","file with output from simulation (may be repeated)");
    parse_args.Add("-l",&out_latex,"format table as LaTeX.");
    parse_args.Add("-t",&tolerance,"tol","omit convergence orders for errors below this.");
    parse_args.Parse();

    PHYSBAM_ASSERT(patterns.m==cols.m && cols.m>0);
    PHYSBAM_ASSERT(resolutions.m==inputs.m && inputs.m>=2);

    rs.Resize(patterns.m);
    for(int i=0;i<patterns.m;i++)
        rs(i)=std::regex(patterns(i));

    const char* divider=out_latex?" & ":" ";
    
    ARRAY<T> prev;
    for(int i=0;i<inputs.m;i++)
    {
        ARRAY<T> cur=Read_Input_File(inputs(i));
        printf("%4i ",resolutions(i));
        for(int p=0;p<patterns.m;p++)
        {
            if(i && abs(prev(p))>tolerance && abs(cur(p))>tolerance)
            {
                T o=Compute_Order(resolutions(i-1),prev(p),resolutions(i),cur(p));
                printf("%s%8.2e%s%5.2f",divider,cur(p),divider,o);
            }
            else
            {
                printf("%s%8.2e%s  -  ",divider,cur(p),divider);
            }
        }
        if(out_latex) printf(" \\\\\n");
        else printf("\n");
        prev=cur;
    }

    return 0;
}
