
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC/LIP6 2008-2010, All Rights Reserved
//
// ===================================================================
//
// $Id$
//
// x-----------------------------------------------------------------x 
// |                                                                 |
// |                   C O R I O L I S                               |
// |          U n i c o r n  -  M a i n   G U I                      |
// |                                                                 |
// |  Author      :                    Jean-Paul CHAPUT              |
// |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./CgtMain.cpp"                            |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x


#include  <csignal>
#include  <memory>
using namespace std;

#include  <boost/program_options.hpp>
namespace boptions = boost::program_options;

#include  <boost/filesystem/operations.hpp>
namespace bfs = boost::filesystem;

#include  "vlsisapd/configuration/Configuration.h"
#include  "hurricane/DebugSession.h"
#include  "hurricane/DataBase.h"
#include  "hurricane/Cell.h"
#include  "hurricane/Warning.h"
#include  "hurricane/UpdateSession.h"
#include  "hurricane/isobar/Script.h"

#include  <QtGui>
#if (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) and not defined (__APPLE__)
#  include  <QGtkStyle>
#endif

#include  "hurricane/viewer/HApplication.h"
#include  "hurricane/viewer/Graphics.h"
using namespace Hurricane;

#include  "crlcore/Utilities.h"
#include  "crlcore/AllianceFramework.h"
#include  "crlcore/Hierarchy.h"
#include  "crlcore/ToolBox.h"
using namespace CRL;

#include  "nimbus/NimbusEngine.h"
using namespace Nimbus;

#include  "metis/MetisEngine.h"
using namespace Metis;

#include  "mauka/GraphicMaukaEngine.h"
using namespace Mauka;

#include  "knik/GraphicKnikEngine.h"
using namespace Knik;

#include  "kite/GraphicKiteEngine.h"
using namespace Kite;

#include  "equinox/GraphicEquinoxEngine.h"
using namespace Equinox;

#include  "solstice/GraphicSolsticeEngine.h"
using namespace Solstice;

#include  "unicorn/UnicornGui.h"
using namespace Unicorn;


// x-----------------------------------------------------------------x
// |                      Fonctions Definitions                      |
// x-----------------------------------------------------------------x


// -------------------------------------------------------------------
// Function  :  "main()".

int main ( int argc, char *argv[] )
{
  int   returnCode  = 0;
  bool  kiteSuccess = true;

  try {
    bfs::path::default_name_check ( bfs::portable_posix_name );

    float         edgeCapacity;
    float         expandStep;
    unsigned long eventsLimit;
    unsigned int  traceLevel;
    bool          textMode;
    double        margin;
    bool          quadriPlace;
    bool          annealingPlace;
    unsigned int  partitionSizeStop;
    bool          globalRoute;
    bool          detailedRoute;
    bool          loadGlobal;
    bool          saveGlobal;

    boptions::options_description options ("Command line arguments & options");
    options.add_options()
      ( "help,h"             , "Print this help." )
      ( "trace-level,l"      , boptions::value<unsigned int>(&traceLevel)
                             , "Set the level of trace, trace messages with a level superior to "
                               "<arg> will be printed on <stderr>." )
      ( "verbose,v"          , boptions::bool_switch()
                             , "First level of verbosity.")
      ( "very-verbose,V"     , boptions::bool_switch()
                             , "Second level of verbosity.")
      ( "info,i"             , boptions::bool_switch()
                             , "Lots of informational messages.")
      ( "show-conf"          , boptions::bool_switch()
                             , "Print Kite configuration settings.")
      ( "conf"               , boptions::value<string>()
                             , "An XML configuration file." )
      ( "core-dump,D"        , boptions::bool_switch()
                             , "Enable core dumping.")
      ( "log-mode,L"         , boptions::bool_switch()
                             , "Disable ANSI escape sequences displaying.")
      ( "text,t"             , boptions::bool_switch(&textMode)->default_value(false)
                             , "Run in pure text mode.")
      ( "margin,m"           , boptions::value<double>(&margin)
                             , "Percentage of free area to add to the minimal placement area.")
      ( "quadri-place,p"     , boptions::bool_switch(&quadriPlace)->default_value(false)
                             , "Place using quadripartitions then placement legalisation.")
      ( "annealing-place,P"  , boptions::bool_switch(&annealingPlace)->default_value(false)
                             , "Place using simulated annealing (slow).")
      ( "partition-size-stop", boptions::value<unsigned int>(&partitionSizeStop)->default_value(45)
                             , "Sets the size of a leaf partition (quadripartition stage).")
      ( "global-route,G"     , boptions::bool_switch(&globalRoute)->default_value(false)
                             , "Run the global router (Knik).")
      ( "load-global,g"      , boptions::bool_switch(&loadGlobal)->default_value(false)
                             , "Reload the global routing from disk.")
      ( "save-global"        , boptions::bool_switch(&saveGlobal)->default_value(false)
                             , "Save the global routing solution.")
      ( "edge,e"             , boptions::value<float>(&edgeCapacity)->default_value(65.0)
                             , "The egde density ratio applied on global router's edges." )
      ( "expand-step"        , boptions::value<float>(&expandStep)->default_value(100.0)
                             , "The density delta above which GCells are aggregateds." )
      ( "events-limit"       , boptions::value<unsigned long>(&eventsLimit)
                             , "The maximum number of iterations (events) that the router is"
                               "allowed to perform." )
      ( "detailed-route,R"   , boptions::bool_switch(&detailedRoute)->default_value(false)
                             , "Run the detailed router (Kite).")
      ( "cell,c"             , boptions::value<string>()
                             , "The name of the cell to load, whithout extension." )
      ( "save-design,s"      , boptions::value<string>()
                             , "Save the routed design.");

    boptions::variables_map arguments;
    boptions::store  ( boptions::parse_command_line(argc,argv,options), arguments );
    boptions::notify ( arguments );

    if ( arguments.count("help") ) {
      cout << options << endl;
      exit ( 0 );
    }

    if ( arguments.count("conf") ) {
      bfs::path userConfFile = arguments["conf"].as<string>();
      if ( bfs::exists(userConfFile) ) {
        Cfg::Configuration* conf = Cfg::Configuration::get ();
        conf->readFromFile ( userConfFile.string() );
      } else {
        cerr << Warning("User defined configuration file:\n  <%s> not found."
                       ,userConfFile.string().c_str()) << endl;
      }
    }

    if (arguments["core-dump"   ].as<bool>()) Cfg::getParamBool("misc.catchCore"    )->setBool ( false );
    if (arguments["verbose"     ].as<bool>()) Cfg::getParamBool("misc.verboseLevel1")->setBool ( true );
    if (arguments["very-verbose"].as<bool>()) Cfg::getParamBool("misc.verboseLevel2")->setBool ( true );
    if (arguments["info"        ].as<bool>()) Cfg::getParamBool("misc.info"         )->setBool ( true );
    if (arguments["log-mode"    ].as<bool>()) Cfg::getParamBool("misc.logMode"      )->setBool ( true );
    if (arguments["show-conf"   ].as<bool>()) Cfg::getParamBool("misc.showConf"     )->setBool ( true );

    if (arguments.count("trace-level" )) Cfg::getParamInt("misc.traceLevel")->setInt ( traceLevel );

    bool showConf = Cfg::getParamBool("misc.showConf")->asBool();

    dbo_ptr<DataBase>          db   ( DataBase::create() );
    dbo_ptr<AllianceFramework> af   ( AllianceFramework::create() );
    Cell*                      cell = NULL;

    if ( arguments.count("cell") ) {
      cell = af->getCell (arguments["cell"].as<string>().c_str(), Catalog::State::Views );
      if (!cell) {
        cerr << af->getPrint() << endl;
        cerr << "[ERROR] Cell not found: " << arguments["cell"].as<string>() << endl;
        exit ( 2 );
      }
    } else {
      quadriPlace    = false;
      annealingPlace = false;
      loadGlobal     = false;
      saveGlobal     = false;
      globalRoute    = false;
      detailedRoute  = false;
    }

    if ( arguments.count("margin") )
      Cfg::getParamPercentage("nimbus.spaceMargin")->setPercentage ( margin );

    if ( arguments.count("partition-size-stop") )
      Cfg::getParamInt("metis.numberOfInstancesStopCriterion")->setInt ( partitionSizeStop );

    if ( arguments.count("edge") )
      Cfg::getParamPercentage("kite.edgeCapacity")->setPercentage ( edgeCapacity );

    if ( arguments.count("events-limit") )
      Cfg::getParamInt("kite.eventsLimit")->setInt ( eventsLimit );

    if ( arguments.count("expand-step") )
      Cfg::getParamPercentage("kite.expandStep")->setPercentage ( expandStep );

    UnicornGui::getBanner().setName    ( "cgt" );
    UnicornGui::getBanner().setPurpose ( "Coriolis Graphical Tool" );

    cmess1 << UnicornGui::getBanner() << endl;
    cmess1 << "        Tool Credits" << endl;
    cmess1 << "        Hurricane .................... Remy Escassut & Christian Masson" << endl;
    cmess1 << "        Nimbus - Infrastructure .......................... Hugo Clement" << endl;
    cmess1 << "        Mauka - Placer ........................... Christophe Alexandre" << endl;
    cmess1 << "        Knik - Global Router ............................ Damien Dupuis" << endl;
    cmess1 << "        Kite - Detailed Router ....................... Jean-Paul Chaput" << endl;
    cmess1 << endl;

    cout   << "        hMETIS software credits" << endl;
    cout   << "        Author ........................................ Georges Karypis" << endl;
    cout   << "        Prof. Ident. .......................... University of Minnesota" << endl;
    cout   << "        URL .......................... http://glaros.dtc.umn.edu/gkhome" << endl;
    cout   << endl;

    cout   << "        The Knik router makes use of FLUTE software" << endl;
    cout   << "        Author ........................................ Chris C. N. CHU" << endl;
    cout   << "        Prof. Ident. ............................ Iowa State University" << endl;
    cout   << "        URL ........................ http://home.eng.iastate.edu/~cnchu" << endl;
    cout   << endl;
    cmess2 << af->getPrint() << endl;

    if ( cell ) {
    // addaccu.
    //DebugSession::addToTrace ( cell, "sel" );
    //DebugSession::addToTrace ( cell, "auxreg3" );
    //DebugSession::addToTrace ( cell, "auxsc21" );
    //DebugSession::addToTrace ( cell, "ram_nwen" );
    //DebugSession::addToTrace ( cell, "acc_o_down" );
    // model_shift.
    //DebugSession::addToTrace ( cell, "c1_2" );
    //DebugSession::addToTrace ( cell, "muxoutput_96" );
    //DebugSession::addToTrace ( cell, "msb" );
    //DebugSession::addToTrace ( cell, "op(0)" );
    //DebugSession::addToTrace ( cell, "i(1)" );
    // rf2r0_32x32.
    //DebugSession::addToTrace ( cell, "busa(0)" );
    // amd2901.
    //DebugSession::addToTrace ( cell, "acc_q(1)" );
    //DebugSession::addToTrace ( cell, "ram_adra(11)" );
    //DebugSession::addToTrace ( cell, "ram_adri(5)" );
    //DebugSession::addToTrace ( cell, "ram_q2(0)" );
    //DebugSession::addToTrace ( cell, "ram_reg5_nckx" );
    //DebugSession::addToTrace ( cell, "acc_reg_nscanx" );
    //DebugSession::addToTrace ( cell, "acc_o_down" );
    //DebugSession::addToTrace ( cell, "nb(3)" );
    // MIPS microprogrammed.
    //DebugSession::addToTrace ( cell, "crsrout_0" );
    //DebugSession::addToTrace ( cell, "dpt_dt_s_30" );
    //DebugSession::addToTrace ( cell, "dpt_wm_rf_dinx_0" );
    //DebugSession::addToTrace ( cell, "dpt_adder_sub_pi_0_10" );
    //DebugSession::addToTrace ( cell, "dpt_ishifter_c0_3" );
    //DebugSession::addToTrace ( cell, "dpt_adder_sub_r_16" );
    //DebugSession::addToTrace ( cell, "dpt_adder_sub_gi_0_24" );
    //DebugSession::addToTrace ( cell, "dpt_ishifter_muxoutput_117" );
    // MIPS pipeline.
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_shift32_rshift_se_muxoutput(98)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_shift32_rshift_se_msb" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_rarith_se(31)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_nxr2_x1_7_sig" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_banc_busa(17)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_instaddbracry_sd_pi_1_17" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_yoper_se(16)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_toper_se(22)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_soper_se(22)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_jadr_sd(14)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_effto_sd(1)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_ioper_rd(30)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_nextpc_rd(1)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_not_kill_se" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_not_opcod_sd_2" );
    //DebugSession::addToTrace ( cell, "rsdnbr_sd(14)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_otheri_sd(25)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_soper_se(31)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_braadr_sd(3)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_seqadr_sd(30)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_data_rm(8)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_not_i_ri(11)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_noa22_x1_61_sig" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_not_opcod_rd(6)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_ct_not_aux190" );
    //DebugSession::addToTrace ( cell, "s_eq_t_sd" );
    //DebugSession::addToTrace ( cell, "i(22)" );
    //DebugSession::addToTrace ( cell, "mips_r3000_1m_dp_mux32_data_o_sm_sel0" );
    // DCT/lvl3
    //DebugSession::addToTrace ( cell, "instance139_fixed_multiplier_mixed_direct_nbit0_16_nbit1_16_specified_report_5_18_0" );
    //DebugSession::addToTrace ( cell, "instance129_fixed_multiplier_mixed_direct_nbit0_16_nbit1_16_cin_i0_true_specified_colout_1_11_2" );
    //DebugSession::addToTrace ( cell, "coef0(7)" );
    //DebugSession::addToTrace ( cell, "c1_0(3)" );
    // DCT/dct_lvl2
    //DebugSession::addToTrace ( cell, "instance156_fixed_multiplier_mixed_direct_nbit0_8_nbit1_14_specified_report_3_13_1" );
    //DebugSession::addToTrace ( cell, "created_net11_0(19)" );
    //DebugSession::addToTrace ( cell, "c11(5)" );
    //DebugSession::addToTrace ( cell, "instance151_fixed_multiplier_mixed_direct_nbit0_8_nbit1_14_cin_i0_true_specified_colout_3_19_1" );
    // idct
    // ieee_division
    //DebugSession::addToTrace ( cell, "ieee_norm_i_norm_shift_i_muxoutput_259" );
    //DebugSession::addToTrace ( cell, "ieee_norm_i_norm_shift_i_muxoutput_113" );
    // eFPGA/04x04.
    //DebugSession::addToTrace ( cell, "group_2_4_nq_mem_45" );
    //DebugSession::addToTrace ( cell, "g_2_1_rig_7" );
    // eFPGA/08x08.
    //DebugSession::addToTrace ( cell, "group_1_7_lut_mux_q01" );
    //DebugSession::addToTrace ( cell, "group_0_5_decoder_vertical_track_0_not_cmd(1)" );
    // eFPGA/16x16.
    //DebugSession::addToTrace ( cell, "group_15_6_clb_topside_in1_mux_q01" );
    //DebugSession::addToTrace ( cell, "g_7_14_top_2" );

    // Python Script test.

      Isobar::Script::addPath ( "/dsk/l1/jpc/coriolis-2.x/work/benchs/routing/addaccu" );

      for ( int i=0 ; i<5 ; ++i ) {
        cerr << "Call:" << i << " ======================================" << endl;

        Isobar::Script* script = Isobar::Script::create ( "getCellName" );
        script->runFunction ( "runScript", cell );
      }

      Box box ( DbU::lambda(0.0), DbU::lambda(0.0), DbU::lambda(5.0), DbU::lambda(10.0) );
      
      // throw Error ("Tu le vois bien: <b>%s</b><br>"
      //             "Un retour a la ligne\n<i>Une ligne</i>"
      //             ,getString(box).c_str() );
    }

    if ( not textMode ) {
      auto_ptr<QApplication> qa ( new HApplication(argc,argv) );
#if (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) and not defined (__APPLE__)
      qa->setStyle ( new QGtkStyle() );
#endif
      Graphics::enable ();

      dbo_ptr<UnicornGui> unicorn ( UnicornGui::create() );
      unicorn->setApplicationName ( QObject::tr("cgt") );

      unicorn->registerTool ( Mauka::GraphicMaukaEngine::grab() );
    //unicorn->registerTool ( Knik::GraphicKnikEngine::grab() );
      unicorn->registerTool ( Kite::GraphicKiteEngine::grab() );
      unicorn->setEnableRedrawInterrupt ( true );
    //unicorn->registerTool ( Equinox::GraphicEquinoxEngine::grab() );
    //unicorn->registerTool ( Solstice::GraphicSolsticeEngine::grab() );

      unicorn->getCellWidget()->setLayerVisible ( "grid", false );
      unicorn->setCell ( cell );
      unicorn->show    ();

      returnCode = qa->exec ();
      ToolEngine::destroyAll ();
    } else {
      if ( quadriPlace or annealingPlace ) {
        loadGlobal  = false;
        globalRoute = true;
      }
      if ( quadriPlace and annealingPlace )
        annealingPlace = false;

      if ( not MetisEngine::isHMetisCapable() and quadriPlace ) {
        cerr << Warning("hMETIS is not avalaible, revert to simulated annealing.") << endl;
        
        annealingPlace = true;
        quadriPlace    = false;
      }

      bool runMaukaTool = quadriPlace or annealingPlace;

      if ( runMaukaTool ) {
        NimbusEngine* nimbus = NULL;
        MetisEngine*  metis  = NULL;
        MaukaEngine*  mauka  = NULL;

        nimbus = NimbusEngine::create ( cell );
        if ( showConf ) nimbus->getConfiguration()->print( cell );

        if ( annealingPlace ) {
          Cfg::getParamPercentage("mauka.standardAnnealing")->setBool ( true );
        }

        if ( quadriPlace ) {
          metis  = MetisEngine ::create ( cell );
          if ( showConf ) metis->getConfiguration()->print( cell );

          MetisEngine::doQuadriPart ( cell );
          MaukaEngine::regroupOverloadedGCells ( cell );
        }

        mauka = MaukaEngine::create ( cell );
        if ( showConf ) mauka->getConfiguration()->print( cell );

        mauka->Run ();
      }

      if ( detailedRoute and not (loadGlobal or globalRoute) ) globalRoute = true;

      bool runKiteTool = loadGlobal or globalRoute or detailedRoute;

      if ( runKiteTool ) {
      //cell->flattenNets ( not arguments.count("global") );

        unsigned int globalFlags = (loadGlobal) ? Kite::LoadGlobalSolution
                                                : Kite::BuildGlobalSolution;

        static KatabaticEngine::NetSet routingNets;
        KiteEngine* kite = KiteEngine::create ( cell );
        if ( showConf ) kite->printConfiguration ();
        
        kite->runGlobalRouter ( globalFlags );
        if ( saveGlobal ) kite->saveGlobalSolution ();

        if ( detailedRoute ) {
          kite->loadGlobalRouting     ( Katabatic::LoadGrByNet, routingNets );
          kite->layerAssign           ( Katabatic::NoNetLayerAssign );
          kite->runNegociate          ();
          kiteSuccess = kite->getToolSuccess ();
          kite->finalizeLayout        ();
          kite->dumpMeasures          ();
          kite->destroy               ();

          if ( arguments.count("save-design") ) {
            cell->setName ( arguments["save-design"].as<string>().c_str() );
            af->saveCell ( cell, Catalog::State::Physical );
          }
        }

        returnCode = (kiteSuccess) ? 0 : 1;
      }
    }
  }
  catch ( Error& e ) {
    cerr << e.what() << endl;
    exit ( 1 );
  }
  catch ( boptions::error& e ) {
    cerr << "[ERROR] " << e.what() << endl;
    exit ( 1 );
  }
  catch ( exception& e ) {
    cerr << "[ERROR] " << e.what() << endl;
    exit ( 1 );
  }
  catch ( ... ) {
    cout << "[ERROR] Abnormal termination: unmanaged exception.\n" << endl;
    exit ( 2 );
  }

  return returnCode;
}
