// ---------------------------------------------------------------------------
// - Ifile.cpp                                                               -
// - afnix cross debugger - instrumented file class implementation           -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2019 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Ifile.hpp"
#include "Runnable.hpp"
#include "Exception.hpp"

namespace afnix {
  
  // this procedure add a form in the line map table if and only if
  // the line is not binded
  static void add_form (QuarkTable& lmap, Form* form) {
    if (form == nullptr) return;
    // get the line number and check
    long lnum = form->getlnum ();
    if (lmap.exists (lnum) == true) return;
    lmap.add (lnum, form);
  }

  // this procedure take a form, traverse it for other form and add them
  // in the line map - of course, the first form wins
  static void map_form (QuarkTable& lmap, Form* form) {
    // add this top level form
    add_form (lmap, form);
    // start to traverse the form
    Cons* cons = form;
    while (cons != nullptr) {
      // get the car and map it to a form
      Form* car = dynamic_cast <Form*> (cons->getcar ());
      if (car != nullptr) map_form (lmap, car);
      // continue with the cdr
      cons = cons->getcdr ();
    }
  }

  // create a new ifile by name with a module
  
  Ifile::Ifile (Module& mp) {
    d_fname = mp.getname ();
    while (true) {
      Form* form = mp.parse ();
      if (form == nullptr) break;
      map_form      (d_lmap, form);
      d_fvec.add (form);
    }
  }

  // destroy this ifile

  Ifile::~Ifile (void) {
    d_lmap.reset ();
    d_fvec.reset ();
  }

  // return the class name
  
  String Ifile::repr (void) const {
    return "Ifile";
  }

  // return the i-file file name

  String Ifile::getfname (void) const {
    rdlock ();
    String result = d_fname;
    unlock ();
    return result;
  }

  // return the form vector length

  long Ifile::length (void) const {
    return d_fvec.length ();
  }

  // get a form by index

  Form* Ifile::getform (const long index) const {
    rdlock ();
    try {
      Form* form = dynamic_cast <Form*> (d_fvec.get (index));
      if (form == nullptr) throw Exception ("ifile-error", "cannot get form");
      unlock ();
      return form;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // find a form by line number

  Form* Ifile::lookup (const long lnum) const {
    if (lnum <  0) throw Exception ("lookup-error", "invalid line number");
    if (lnum == 0) return getform (0);
    rdlock ();
    try {
      // check if the form exists
      if (d_lmap.exists (lnum) == false) {
	unlock ();
	return nullptr;
      }
      Form* form = dynamic_cast <Form*> (d_lmap.get (lnum));
      unlock ();
      return form;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // evaluate this ifile withing the calling nameset
  
  Object* Ifile::eval (Runnable* robj, Nameset* nset) {
    long len = d_fvec.length ();
    Object* result = nullptr;
    for (long i = 0; i < len; i++) {
      Object* obj = d_fvec.get (i);
      if (obj == nullptr) continue;
      if (robj->getnext () == true) {
	robj->setnext (false);
	robj->bpt (nset, obj);
      }
      result = obj->eval (robj, nset);
      robj->post (result);
    }
    return result;
  }
}
