#include "symbol_info.hpp"

#include <ruby.h>

#include <stdexcept>

// Construct a new Symbol_Info from a name, cusip, and previous day's
// closing price
Symbol_Info::
Symbol_Info(
    std::string const & name,
    std::string const & cusip,
    Price previous_closing_price)
  : name(name)
  , cusip(cusip)
  , previous_closing_price(previous_closing_price)
{
}

// Lookup info for a given stock symbol
Symbol_Info_shptr lookup_symbol_info(std::string name)
{
  Symbol_Info_shptr info;

  if(name == "MSFT")
  {
    return Symbol_Info_shptr(new Symbol_Info(
            "MSFT",
            "594918104",
            36.81));
  }
  else if(name == "GOOG")
  {
    return Symbol_Info_shptr(new Symbol_Info(
            "GOOG",
            "38259P508",
            707.00));
  }
  else
  {
    throw std::invalid_argument("Unknown security");
  }
}

// SymbolInfo class
VALUE rb_cSymbolInfo;

extern "C"
void free_symbol_info(Symbol_Info_shptr * symbol_info)
{
  delete symbol_info;
}

// Create a new SymbolInfo object by looking it up and wrapping the
// returned shptr
extern "C"
VALUE symbol_info_s_new(VALUE /* klass */, VALUE name_v)
{
  try
  {
    std::string name(StringValueCStr(name_v));

    Symbol_Info_shptr symbol_info(lookup_symbol_info(name));

    return Data_Wrap_Struct(
        rb_cSymbolInfo,
        0,
        free_symbol_info,
        new Symbol_Info_shptr(symbol_info)
        );
  }
  catch(std::invalid_argument const & ex)
  {
    rb_raise(rb_eArgError, "%s", ex.what());
  }
  catch(std::exception const & ex)
  {
    rb_raise(rb_eRuntimeError, "%s", ex.what());
  }
  catch(...)
  {
    rb_raise(rb_eRuntimeError, "Unknown exception");
  }
}

// Get the previous day's closing price from a SymbolInfo object
extern "C"
VALUE symbol_info_previous_closing_price(VALUE self)
{
  Symbol_Info_shptr * symbol_info;
  Data_Get_Struct(self, Symbol_Info_shptr, symbol_info);
  return rb_float_new((*symbol_info)->previous_closing_price);
}

extern "C"
VALUE symbol_info_marshal_dump(VALUE self)
{
  VALUE array;
  Symbol_Info_shptr * symbol_info;
  Data_Get_Struct(self, Symbol_Info_shptr, symbol_info);

  array = rb_ary_new();
  rb_ary_push(array, rb_str_new2((*symbol_info)->name.c_str()));
  rb_ary_push(array, rb_str_new2((*symbol_info)->cusip.c_str()));
  rb_ary_push(array, rb_float_new((*symbol_info)->previous_closing_price));

  return rb_marshal_dump(array, Qnil);
}

extern "C"
VALUE symbol_info_marshal_load(VALUE self, VALUE str)
{
  VALUE array = rb_marshal_load(str);

  VALUE name = rb_ary_entry(array, 0);
  VALUE cusip = rb_ary_entry(array, 1);
  VALUE previous_closing_price = rb_ary_entry(array, 2);

  rb_check_type(previous_closing_price, T_FLOAT);

  Symbol_Info * symbol_info(new Symbol_Info(
          StringValueCStr(name),
          StringValueCStr(cusip),
          RFLOAT(previous_closing_price)->value));

  DATA_PTR(self) = new Symbol_Info_shptr(symbol_info);

  return self;
}

// Initialize the extension
extern "C"
void Init_symbol_info()
{
  rb_cSymbolInfo = rb_define_class(
      "SymbolInfo",
      rb_cObject);
  rb_define_singleton_method(
      rb_cSymbolInfo,
      "new",
      RUBY_METHOD_FUNC(symbol_info_s_new),
      1);
  rb_define_method(
      rb_cSymbolInfo,
      "previous_closing_price",
      RUBY_METHOD_FUNC(symbol_info_previous_closing_price),
      0);
  rb_define_method(
      rb_cSymbolInfo,
      "marshal_dump",
      RUBY_METHOD_FUNC(symbol_info_marshal_dump),
      0);
  rb_define_method(
      rb_cSymbolInfo,
      "marshal_load",
      RUBY_METHOD_FUNC(symbol_info_marshal_load),
      1);
}

