#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;

static 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));

    // TODO: Still not quite exception-safe (what if Data_Wrap_Struct
    // fails?)
    // Answer: it should only fail on out-of-memory, and then we're in
    // trouble anyway.  To get it perfectly right would require wrapping
    // it in a call to rb_protect, but that's too much.
    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);
}

// 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);
}

// vim:ft=cpp
