/* -*- c++ -*-
 *
 * SOCLIB_LGPL_HEADER_BEGIN
 * 
 * This file is part of SoCLib, GNU LGPLv2.1.
 * 
 * SoCLib is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; version 2.1 of the License.
 * 
 * SoCLib is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with SoCLib; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * SOCLIB_LGPL_HEADER_END
 *
 * Copyright (c) Telecom ParisTech, Comelec 
 *         Jeremie Brunel <jeremie.brunel@telecom-paristech.fr>, 2013
 *
 */

#include "vcd_parser.h"
#include <iostream>
#include <sstream>
#include <stdlib.h>

using namespace std;

namespace soclib
{
  namespace common
  {
    VcdParser::VcdParser(const string &filename)
    {
      string line;
      m_file.open(filename.c_str(), ifstream::in);

      while (getline(m_file, line))
      {
        if (line.compare(0,4,"$var")==0)
          break;
      }

      do
      {
        if (line.compare(0,4,"$var"))
          break;

        stringstream parse(line);
        string var;
        string type;
        uint32_t size;
        string key;
        string name;
        string be;

        parse >> var; 
        parse >> type;
        parse >> size;
        parse >> key;
        parse >> name;
        parse >> be;

        pair<string, uint32_t> value(name, 0);
        m_keys_table.insert(pair<string, pair<string, uint32_t> >(key, value));
      }
      while (getline(m_file, line));

      while (getline(m_file, line))
      {
        if (line.compare(0,9,"$dumpvars")==0)
          break;
      }

      while (getline(m_file, line))
      {
        if (line.compare(0,4,"$end")==0)
        {
          break;
        }

        pair<string, uint32_t> read = parse_line(line);
        m_keys_table[read.first].second = read.second;
      }
    }

    pair<string, uint32_t> VcdParser::parse_line(string &line)
    {
      size_t pos;
      string key, tmp;
      uint32_t val;

      if (line[0] == 'b')
      {
        pos = line.find_first_of(' ', 0);
        tmp = line.substr(1,pos-1);
        val = strtol(tmp.c_str(), NULL, 2);
        key = line.substr(pos+1, line.size()-(pos+1));
      }
      else
      {
        if (line[0]=='0')
          val = 0;
        else
          val = 1;

        key = line.substr(1,line.size()-1);
      }

      return pair<string, uint32_t>(key, val);
    }

    map<string, uint32_t> VcdParser::construct_map()
    {
      map<string, uint32_t> wave_map;
      for (map<string, pair<string, uint32_t> >::iterator it = m_keys_table.begin();
          it != m_keys_table.end(); it++)
      {
        wave_map.insert(it->second);
      }
      return wave_map;
    }

    map<string, uint32_t> VcdParser::get_next_wave(uint32_t *time, bool clean, string key, uint32_t val)
    {
      if (clean)
      {
        do
        {
          map<string, uint32_t> tmp = next_wave(time);
          if (tmp.empty())
            return map<string, uint32_t>();
          
          if (tmp[key] == val)
            return next_wave(time);
        }
        while (1);

      }
      else
      {
        return next_wave(time); 
      }
    }

    map<string, uint32_t> VcdParser::next_wave(uint32_t *time)
    {
      //std::cout << "---------------get_next_wave--------------------\n";
      string line;
      size_t nb_modif = 0;

      while (getline(m_file, line))
      {
        if (line[0]=='#')
        {
          *time = strtol(line.substr(1, line.size()-1).c_str(), NULL, 10);
          break;
        }
      }

      while (getline(m_file, line))
      {
        if (line.empty())
          break;

        pair<string, uint32_t> read = parse_line(line);
        m_keys_table[read.first].second = read.second;
        nb_modif++;
      }

      //cout << "parser " << *time << endl;
      if (nb_modif)
        return construct_map();
      else
        return map<string, uint32_t>();
    }
    
    VcdParser::~VcdParser()
    {
      m_file.close();
    }
  }
}
