/***********************************************************************
 * Generating paper re-submission diagram (for VLSI CAD and FPGA confs)
 * Author:  Yu Hu (yuhu@ucla.edu), University of California Los Angeles
 * Date:    Mar. 11th, 2007
***********************************************************************/

#include <iostream>
#include <vector>

using namespace std;

typedef struct _conference {
    char* name;
    char* node_name;
    char* due_date;
    char* accept_date;
    vector<struct _conference*>* fanouts;
} conference;

conference glb_all_confs [] = {
    /* second year */
    {"SLIP"           , "n2_SLIP"           ,"11-24-0001",     "01-09-0002", NULL}, 
    {"DAC"            , "n2_DAC"            ,"11-20-0001",     "03-09-0002", NULL},
    {"GLSVLSI"        , "n2_GLSVLSI"        ,"11-10-0001",     "12-22-0001", NULL},
    {"ISPD"           , "n2_ISPD"           ,"10-12-0001",     "11-18-0001", NULL},
    {"ISCAS"          , "n2_ISCAS"          ,"10-06-0001",     "01-05-0002", NULL},
    {"ISQED"          , "n2_ISQED"          ,"09-30-0001",     "11-21-0001", NULL},
    {"FPGA"           , "n2_FPGA"           ,"09-15-0001",     "11-20-0001", NULL},
    {"DATE"           , "n2_DATE"           ,"09-10-0001",     "11-10-0001", NULL},
    {"ASPDAC"         , "n2_ASPDAC"         ,"07-10-0001",     "09-30-0001", NULL},
    {"ICCAD"          , "n2_ICCAD"          ,"04-11-0001",     "06-20-0001", NULL},
    {"CICC"           , "n2_CICC"           ,"04-09-0001",     "06-08-0001", NULL},
    {"FPL"            , "n2_FPL"            ,"03-18-0001",     "05-21-0001", NULL},
    {"IWLS"           , "n2_IWLS"           ,"03-10-0001",     "04-13-0001", NULL},
    {"ISLPED"         , "n2_ISLPED"         ,"03-02-0001",     "05-10-0001", NULL},
    {"MWSCAS"         , "n2_MWSCAS"         ,"03-02-0001",     "05-08-0001", NULL},
    {"FCCM"           , "n2_FCCM"           ,"01-12-0001",     "03-01-0001", NULL},
    /* first year */    
    {"SLIP"           , "n1_SLIP"           ,"11-24-0000",     "01-09-0001", NULL}, 
    {"DAC"            , "n1_DAC"            ,"11-20-0000",     "03-09-0001", NULL},
    {"GLSVLSI"        , "n1_GLSVLSI"        ,"11-10-0000",     "12-22-0000", NULL},
    {"ISPD"           , "n1_ISPD"           ,"10-12-0000",     "11-18-0000", NULL},
    {"ISCAS"          , "n1_ISCAS"          ,"10-06-0000",     "01-05-0001", NULL},
    {"ISQED"          , "n1_ISQED"          ,"09-30-0000",     "11-21-0000", NULL},
    {"FPGA"           , "n1_FPGA"           ,"09-15-0000",     "11-20-0000", NULL},
    {"DATE"           , "n1_DATE"           ,"09-10-0000",     "11-10-0000", NULL},
    {"ASPDAC"         , "n1_ASPDAC"         ,"07-10-0000",     "09-30-0000", NULL},
    {"ICCAD"          , "n1_ICCAD"          ,"04-11-0000",     "06-20-0000", NULL},
    {"CICC"           , "n1_CICC"           ,"04-09-0000",     "06-08-0000", NULL},
    {"FPL"            , "n1_FPL"            ,"03-18-0000",     "05-21-0000", NULL},
    {"IWLS"           , "n1_IWLS"           ,"03-10-0000",     "04-13-0000", NULL},
    {"ISLPED"         , "n1_ISLPED"         ,"03-02-0000",     "05-10-0000", NULL},
    {"MWSCAS"         , "n1_MWSCAS"         ,"03-02-0000",     "05-08-0000", NULL},
    {"FCCM"           , "n1_FCCM"           ,"01-12-0000",     "03-01-0000", NULL},

};

char* glb_monthes [] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "July", "Aug",
    "Sep", "Oct", "Nov", "Dec"};

int comp_date(char* date1, char* date2)
{
    char* str_year1 = strdup(date1+6);
    char* str_year2 = strdup(date2+6);
    char* str_mon1  = strdup(date1);    str_mon1[2] = 0;
    char* str_mon2  = strdup(date2);    str_mon2[2] = 0;
    char* str_day1  = strdup(date1+3);  str_day1[5] = 0;
    char* str_day2  = strdup(date2+3);  str_day2[5] = 0;

    int year1 = atoi(str_year1);
    int year2 = atoi(str_year2);
    int mon1  = atoi(str_mon1);
    int mon2  = atoi(str_mon2);
    int day1  = atoi(str_day1);
    int day2  = atoi(str_day2);

    //cout << date1 << ": " << year1 << "," << mon1 << "," << day1 << endl;
    //cout << date2 << ": " << year2 << "," << mon2 << "," << day2 << endl;

    free(str_year1); free(str_year2); free(str_mon1); 
    free(str_mon2); free(str_day1); free(str_day2);

    if(year1 > year2) return 1;
    if(year1 < year2) return -1;

    if(mon1  > mon2)  return 1;
    if(mon1  < mon2)  return -1;

    if(day1  > day2)  return 1;
    if(day1  < day2)  return -1;

    return 0;

}

int can_resubmit(conference* rej, conference* next)
{
    char* date1 = rej->accept_date;
    char* date2 = next->due_date;

    if(comp_date(date1, date2) == -1)
        return 1;
    return 0;
}

bool is_redudant_fanout(conference* rej, conference* next, conference* sib)
{
    // next is redudant if there is a path "rej->sib->next"
    // i.e., rej->accept_date < sib->due_date < sib->accept_date < next->due_date

    if(comp_date(rej->accept_date, sib->due_date) == -1 &&
            comp_date(sib->accept_date, next->due_date) == -1)
        return true;
    return false;
}

bool is_redundant(conference* rej, conference* cur_conf, vector<conference*> *sibling)
{
    for(vector<conference*>::iterator conf_ptr = sibling->begin();
            conf_ptr != sibling->end(); ++ conf_ptr){
        if(*conf_ptr == cur_conf) continue;
        conference* cur_sib = *conf_ptr;
        if(is_redudant_fanout(rej, cur_conf, cur_sib)) return true;
    }
    return false;
}

void setup_fanouts()
{
    int num_confs = sizeof(glb_all_confs) / sizeof(conference);

    // setup all fanouts
    for(int i=0;i<num_confs;i++){
        conference* rej_conf = &glb_all_confs[i];
        rej_conf->fanouts = new vector<conference*>;
        for(int j=0;j<num_confs;j++){
            conference* next_conf = &glb_all_confs[j];
            if(can_resubmit(rej_conf, next_conf))
                rej_conf->fanouts->push_back(next_conf);
        }
    }

    // refine fanouts
    for(int i=0;i<num_confs;i++){
        conference* rej = &glb_all_confs[i];
        vector<conference*> *new_fanouts = new vector<conference*>;
        for(vector<conference*>::iterator conf_ptr = rej->fanouts->begin();
                conf_ptr != rej->fanouts->end(); ++ conf_ptr){
            conference* cur_conf = *conf_ptr;
            if(!is_redundant(rej, cur_conf, rej->fanouts))
                new_fanouts->push_back(cur_conf);
        }
        delete rej->fanouts;
        rej->fanouts = new_fanouts;
    }

}

int get_conf_month_year(conference* conf, int &ret_mon, int &ret_year)
{
    char* date = conf->due_date;
    char* str_mon  = strdup(date);    str_mon[2] = 0;

    int mon  = atoi(str_mon);
    int year = date[strlen(date)-1]-'0';    // year only can be 0000, 0001, 0002

    free(str_mon); 

    ret_mon = mon; ret_year = year;

    int num_monthes = sizeof(glb_monthes)/sizeof(char*);
    return mon+num_monthes*year-1;
}

void dot_head()
{
    cout << "digraph G {" << endl;
    cout << "ranksep=.75; size = \"15,15\";" << endl;
    cout << "{" << endl;
    cout << "node [shape=plaintext, fontsize=32];" << endl;

    // years
    int num_monthes = sizeof(glb_monthes)/sizeof(char*);
    for(int i=0;i<num_monthes;i++)
        cout << glb_monthes[i] << "_year1 ->";
    for(int i=0;i<num_monthes-1;i++)
        cout << glb_monthes[i] << "_year2 ->";
    cout << glb_monthes[num_monthes-1] << "_year2;";
    cout << endl << "}" << endl;

    cout << endl;

    // nodes
    int num_confs = sizeof(glb_all_confs) / sizeof(conference);
    for(int i=0;i<num_confs;i++){
        conference* rej_conf = &glb_all_confs[i];
        cout << "node [fontsize=32, label=\"" << rej_conf->name << "\"]";
        cout << "\t\t" << rej_conf->node_name << endl;
    }

    cout << endl;

    // rank
    vector<conference*> *confs_in_month = new vector<conference*>[num_monthes*2];
    for(int i=0;i<num_confs;i++){
        conference* rej_conf = &glb_all_confs[i];
        int ret_mon, ret_year;
        int mon = get_conf_month_year(rej_conf, ret_mon, ret_year);
        confs_in_month[mon].push_back(rej_conf);
    }
    for(int i=0;i<num_monthes*2;i++){
        vector<conference*>& confs = confs_in_month[i];
        if(confs.size() == 0) continue;
        int ret_mon, ret_year;
        int mon = get_conf_month_year(confs[0], ret_mon, ret_year);
        cout << "{rank = same; " << glb_monthes[ret_mon-1] << "_year" <<
            ret_year+1 << "; ";
        for(vector<conference*>::iterator conf_ptr = confs.begin();
                conf_ptr != confs.end(); ++ conf_ptr)
            cout << (*conf_ptr)->node_name << "; " ;
        cout << "}" << endl;
    }

    cout << endl;

    // conference nodes
    setup_fanouts();
    
    // draw edges
    for(int i=0;i<num_confs;i++){
        conference* rej = &glb_all_confs[i];
        cout << "// resubmission for " << rej->name << endl;
        for(vector<conference*>::iterator conf_ptr = rej->fanouts->begin();
                conf_ptr != rej->fanouts->end(); ++ conf_ptr){
            cout << rej->node_name << " -> " << (*conf_ptr)->node_name <<
                endl;
        }
        cout << endl;
    }

}

void dot_tail()
{
    cout << "}";
}

int main(int argc, char* argv[])
{
    dot_head();

    dot_tail();
}
