
#ifndef _TMS320C6X
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#endif

#include <cstddef>
#include <string.h>

#include "mex.h"
#include "gc_mex.h"

#include "gc_config.h"
#include "gc_algorithms.h"

#include "GcObject.h"
#include "GcGraphicItem.h"

#include "vg_driver.h"

#define cpp_offsetof(class_, field_)  (((unsigned int)(&(((class_*)0x10)->field_)))-0x10)

/*

static const mex_mt_object_t gc_logpal_str[] =
{
  //{ 0, "color#", &mex_mt_int, 0, GcLogicPalette::num_of_logical_colors },
  { 0, "color", &mex_mt_int, (void*)cpp_offsetof(GcLogicPalette, ctab), 8}, //(void*)gc_mex_cpp_offsetof(GcLogicPalette, ctab), 0}, //GcLogicPalette::num_of_logical_colors },

  {0}
};


static const mex_mt_struct_t mex_mt_gc_logpal =
{
  { "gclpal", 0, &mex_mt_struct_methods }, gc_logpal_str
};
const mex_mt_tag_t* GcLogicPalette::mexMetatype() const
{
  return &MEX_MT_CAST(mex_mt_gc_logpal);
}
*/
class GcObjMonFriend_GcObject
{
public:
  static const mex_mt_object_t gco_lists[];
  static const mex_mt_struct_t gco_mt;
};

const mex_mt_object_t GcObjMonFriend_GcObject::gco_lists[]=
{
  { 0, "name", &mex_mt_cstr, (void*)cpp_offsetof(GcObject, gco_name_ptr_), 0, mex_mt_obj_read_only},
  { 0, "inst#", &mex_mt_uint, (void*)cpp_offsetof(GcObject, gco_instance_num_), 0, mex_mt_obj_read_only},
  MEX_TABLE_END
};

const mex_mt_struct_t GcObjMonFriend_GcObject::gco_mt=
{{"gcobj", 0, &mex_mt_struct_methods}, gco_lists};

class GcObjMonFriend_GcGraphicItem
{
public:
  static const mex_mt_object_t gci_lists[];
  static const mex_mt_struct_t gci_mt;
};

const mex_mt_object_t GcObjMonFriend_GcGraphicItem::gci_lists[]=
{
  { 0, "name", &mex_mt_cstr, (void*)cpp_offsetof(GcObject, gco_name_ptr_), 0, mex_mt_obj_read_only},
  { 0, "visible", &mex_mt_uint, (void*)cpp_offsetof(GcGraphicItem, isVisible), 0},
  { 0, "active", &mex_mt_uint, (void*)cpp_offsetof(GcGraphicItem, isActive), 0},
  MEX_TABLE_END
};

const mex_mt_struct_t GcObjMonFriend_GcGraphicItem::gci_mt=
{{"gcitem", 0, &mex_mt_struct_methods}, gci_lists};

/*
static const mex_mt_object_t gci_lists[]=
{
  { 0, "name", &mex_mt_cstr, (void*)cpp_offsetof(GcObject, gco_name_ptr_), 0, mex_mt_obj_read_only},
  { 0, "visible", &mex_mt_uint, (void*)cpp_offsetof(GcGraphicItem, isVisible), 0},
  { 0, "active", &mex_mt_uint, (void*)cpp_offsetof(GcGraphicItem, isActive), 0},
  MEX_TABLE_END
};

static const mex_mt_struct_t gci_mt=
{{"gcitem", 0, &mex_mt_struct_methods}, gci_lists};
*/

#if 0
const mex_mt_tag_t* GcObject::mexMetatype() const
{
  /*
  if (!gco_mex_metatype_)
    return mexBaseClassMetatype();
  return gco_mex_metatype_;
  */
  return &MEX_MT_CAST(gco_mt);
}
#endif

//Reference Systems
#include "gc_referencesystems.h"

static float calc_az;
static float calc_rng;
static float calc_rng_scale;
static float calc_ptaz;
static int calc_rad;
static int calc_meters;


static mex_res_t CmdPpiPixXY()
{
  mex_mt_reference_t* obj=mex_object_get_current();

  GcReferenceSystem& rs=*reinterpret_cast<GcReferenceSystem*>(obj->base);
  int x;
  int y;

  bool rad=mex_param_is_present(&calc_rad);
  bool meters=mex_param_is_present(&calc_meters);

  if (!rad)
    calc_az=gc_math::deg_to_rad(calc_az);

  if (!meters)
    calc_rng=gc_math::nm_to_meters(calc_rng);

  bool override=false;

  if (mex_param_is_present(&calc_rng_scale))
  {
    if (!meters)
      calc_rng_scale=gc_math::nm_to_meters(calc_rng_scale);
    override=true;
  }
  else
    calc_rng_scale=gc_math::Calc.range_scale();

  if (mex_param_is_present(&calc_ptaz))
  {
    if (!rad)
      calc_ptaz=gc_math::deg_to_rad(calc_ptaz);
    override=true;
  }
  else
    calc_ptaz=gc_math::Calc.platform_aizmuth();

  //if (override)
  //  gc_math::ppi_pix_xy(&x, &y, calc_rng, calc_rng_scale, calc_az, calc_ptaz);
  //else
  //  gc_math::Calc.ppi_pix_xy(x,y, calc_rng, calc_az);
  if (override)
    rs.pix_xy(x, y, calc_rng, calc_az,calc_ptaz, calc_rng_scale);
  else
    rs.pix_xy(x, y, calc_rng, calc_az);

  float rng_sc_nm=gc_math::meters_to_nm(calc_rng_scale);
  float ptaz_deg=gc_math::rad_to_deg(calc_ptaz);
  float rng_nm=gc_math::meters_to_nm(calc_rng);
  float az_deg=gc_math::rad_to_deg(calc_az);

  int abs_x=gc_math::calculator::x_to_abs(x);
  int abs_y=gc_math::calculator::y_to_abs(y);

  mex_printf("pix_xy%c(%g[%.3gd], %g[%.3gnm], %g[%.3gd], %g[%.3gnm])= %d,%d (%d,%d)\n",
    override ? '*' : ' ',
    calc_az, az_deg,
    calc_rng, rng_nm,
    calc_ptaz, ptaz_deg,
    calc_rng_scale, rng_sc_nm,
    x, y, abs_x, abs_y);

  return mex_ok;
}
static const mex_cmd_param_descriptor_t cmd_ppixy_params[] =
{
  { "az", "azimuth", mex_cmd_required, &calc_az, &mex_mt_float},
  { "rng", "range", mex_cmd_required, &calc_rng, &mex_mt_float},

  { "scale", "override range scale", mex_cmd_qualifier|mex_cmd_required_value, &calc_rng_scale, &mex_mt_float},
  { "ptaz", "override platform azimuth", mex_cmd_qualifier|mex_cmd_required_value, &calc_ptaz, &mex_mt_float},
  { "r", "rad", mex_cmd_qualifier, &calc_rad},
  { "m", "meters", mex_cmd_qualifier, &calc_meters},

  { 0 }
};
static const mex_cmd_descriptor_t cmd_ppixy=
{ "ppixy", "(x,y)=ppi_xy(ax, rng)", (mex_cmd_handler_t) CmdPpiPixXY, cmd_ppixy_params};


static mex_res_t CmdAzElPix()
{
  mex_mt_reference_t* obj=mex_object_get_current();

  GcReferenceSystem& rs=*reinterpret_cast<GcReferenceSystem*>(obj->base);

  bool rad=mex_param_is_present(&calc_rad);

  if (!rad)
  {
    calc_az=gc_math::deg_to_rad(calc_az);
    calc_rng=gc_math::deg_to_rad(calc_rng);
  }

  float el_deg=gc_math::rad_to_deg(calc_rng);
  float az_deg=gc_math::rad_to_deg(calc_az);

  int x=rs.az_pix(calc_az);
  int y=rs.el_pix(calc_rng);

  mex_printf("azel_pix(%g[%.3gd], %g[%.3gnm])= %d,%d (X_AZI_SCA=%d, PIX_AZI_SCA=%d\n",
    calc_az, az_deg,
    calc_rng, el_deg,
    x, y,
    gc_cfg::X_AZI_SCA, gc_cfg::PIX_AZI_SCA);

  return mex_ok;
}

static const mex_cmd_descriptor_t cmd_azel_pix=
{ "azel_pix", "(x,y)=azel_pix(az, el)", (mex_cmd_handler_t) CmdAzElPix, cmd_ppixy_params};

static const mex_mt_object_t mex_rs_list[]=
{
  MEX_CMD_REF("pix_xy", &cmd_ppixy),
  MEX_CMD_REF("azel_pix", &cmd_azel_pix),

  MEX_TABLE_END
};

static const mex_mt_struct_t rs_mt=
{{"rs", 0, &mex_mt_struct_methods}, mex_rs_list};

//extern const mex_mt_t* const mex_rs_mt_=&MEX_MT_CAST(rs_mt);
//const mex_mt_tag_t* GcReferenceSystem::mexMetatype() const
//{
//  return &MEX_MT_CAST(rs_mt);
//}

//Configuration & calculator
#ifdef GC_GEOMETRY_SMART

#define CFG_STORE(x_)	  { 0, # x_, &mex_mt_int, &gc_cfg:: x_, 0, 0}

static struct
{
	int X_MAX_PHY;
	int Y_MAX_PHY;
	int X_MAX_STRIDE;
} config_store=
{
		gc_cfg_k::K_X_MAX_PHYSICAL_DISPLAY_DIMENSION,
		gc_cfg_k::K_Y_MAX_PHYSICAL_DISPLAY_DIMENSION,
		gc_cfg_k::K_X_MAX_PHYSICAL_DISPLAY_STRIDE
};

#else

#define CFG_STORE(x_)	config_store.x_

static struct
{
  int x_phy_dim;
  int y_phy_dim;

  int x_abs_offset;
  int y_abs_offset;

  int x_abs_dim;
  int y_abs_dim;
  int x_abs_centre;
  int y_abs_centre;

  int x_mov_offset;
  int y_mov_offset;
  int x_mov_dim;
  int y_mov_dim;
  int x_mov_centre;
  int y_mov_center;

  int y_zero_rng;
  int y_zero_rng_ppi;

} config_store =
{
  gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION,
  gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION,

  gc_cfg::X_LOGICAL_DISPLAY_OFFSET,
  gc_cfg::Y_LOGICAL_DISPLAY_OFFSET,

  gc_cfg::X_ABS_DIMENSION,
  gc_cfg::Y_ABS_DIMENSION,
  gc_cfg::X_ABS_CENTRE_DISPL,
  gc_cfg::Y_ABS_CENTRE_DISPL,

  gc_cfg::X_MOVWIN_OFFSET,
  gc_cfg::Y_MOVWIN_OFFSET,
  gc_cfg::X_DIMENSION,
  gc_cfg::Y_DIMENSION,
  gc_cfg::X_CENTRE_DISPL,
  gc_cfg::Y_CENTRE_DISPL,

  gc_cfg::Y_ZERO_RANGE,
  gc_cfg::Y_ZERO_RNG_PPI
};
#endif


/*
static mex_res_t CmdPpiPixXY()
{
  int x;
  int y;

  bool rad=mex_param_is_present(&calc_rad);
  bool meters=mex_param_is_present(&calc_meters);

  if (!rad)
    calc_az=gc_math::deg_to_rad(calc_az);

  if (!meters)
    calc_rng=gc_math::nm_to_meters(calc_rng);

  bool override=false;

  if (mex_param_is_present(&calc_rng_scale))
  {
    if (!meters)
      calc_rng_scale=gc_math::nm_to_meters(calc_rng_scale);
    override=true;
  }
  else
    calc_rng_scale=gc_math::Calc.range_scale();

  if (mex_param_is_present(&calc_ptaz))
  {
    if (!rad)
      calc_ptaz=gc_math::deg_to_rad(calc_ptaz);
    override=true;
  }
  else
    calc_ptaz=gc_math::Calc.platform_aizmuth();

  if (override)
    gc_math::ppi_pix_xy(&x, &y, calc_rng, calc_rng_scale, calc_az, calc_ptaz);
  else
    gc_math::Calc.ppi_pix_xy(x,y, calc_rng, calc_az);

  float rng_sc_nm=gc_math::meters_to_nm(calc_rng_scale);
  float ptaz_deg=gc_math::rad_to_deg(calc_ptaz);
  float rng_nm=gc_math::meters_to_nm(calc_rng);
  float az_deg=gc_math::rad_to_deg(calc_az);

  int abs_x=gc_math::calculator::x_to_abs(x);
  int abs_y=gc_math::calculator::y_to_abs(y);

  mex_printf("ppi_xy%c(%g[%.3gd], %g[%.3gnm], %g[%.3gd], %g[%.3gnm])= %d,%d (%d,%d)\n",
    override ? '*' : ' ',
    calc_az, az_deg,
    calc_rng, rng_nm,
    calc_ptaz, ptaz_deg,
    calc_rng_scale, rng_sc_nm,
    x, y, abs_x, abs_y);

  return mex_ok;
}


static const mex_cmd_param_descriptor_t cmd_ppixy_params[] =
{
  { "az", "azimuth", mex_cmd_required, &calc_az, &mex_mt_float},
  { "rng", "range", mex_cmd_required, &calc_rng, &mex_mt_float},

  { "scale", "override range scale", mex_cmd_qualifier|mex_cmd_required_value, &calc_rng_scale, &mex_mt_float},
  { "ptaz", "override platform azimuth", mex_cmd_qualifier|mex_cmd_required_value, &calc_ptaz, &mex_mt_float},
  { "r", "rad", mex_cmd_qualifier, &calc_rad},
  { "m", "meters", mex_cmd_qualifier, &calc_meters},

  { 0 }
};
static const mex_cmd_descriptor_t cmd_ppixy=
{ "ppixy", "(x,y)=ppi_xy(ax, rng)", (mex_cmd_handler_t) CmdPpiPixXY, cmd_ppixy_params};
*/

#ifdef GC_GEOMETRY_SMART

static int calc_dx;

#include "SoftScanConverterDriver.h"

extern void gc_geometry_recalculate(int pyh_dx, int phy_dx, int rdr_dxy, int x_offset, int y_offset);
extern void GcEngineResize();

#include "bsp_interrupts.h"

static mex_res_t CmdResize()
{
	int dx=calc_dx;
	if ((dx<64) || (dx>gc_cfg_k::K_XY_MAX_LOGICAL_DISPLAY_SIZE))
	{
		return mex_err_invalid_argument;
	}
	if (dx & 0x3)
	{
		return mex_err_invalid_argument;
	}

	SoftScanConverterDriver::block();
	unsigned int ists=bsp_interrupts_disable();

	gc_geometry_recalculate(-1, -1, dx, -1, -1);

	SoftScanConverterDriver::resize(-1, -1, -1);

	GcEngineResize();

	vg_drv_resize(gc_cfg::X_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::Y_PHYSICAL_DISPLAY_DIMENSION, gc_cfg::X_PHYSICAL_DISPLAY_STRIDE);
	bsp_interrupts_restore(ists);

	SoftScanConverterDriver::unblock();

	return mex_ok;
}

static const mex_cmd_param_descriptor_t cmd_resize_params[] =
{
  { "dx", "dx", mex_cmd_required, &calc_dx, &mex_mt_int},
  { 0 }
};

static const mex_cmd_descriptor_t cmd_resize =
{ "resize", "resize display", (mex_cmd_handler_t) CmdResize, cmd_resize_params };
#endif

static const mex_mt_object_t gc_cfg_lists[]=
{
#ifdef 	GC_GEOMETRY_SMART
	CFG_STORE(X_PHYSICAL_DISPLAY_DIMENSION),
	CFG_STORE(Y_PHYSICAL_DISPLAY_DIMENSION),
	CFG_STORE(XY_LOGICAL_DISPLAY_DIMENSION),
    CFG_STORE(X_LOGICAL_DISPLAY_OFFSET),
    CFG_STORE(Y_PHYSICAL_DISPLAY_OUTPUT),
    CFG_STORE(X_PHYSICAL_DISPLAY_STRIDE),

    { 0, "K_X_MAX_PHY", &mex_mt_int, &config_store.X_MAX_PHY, 0, mex_mt_obj_read_only},
    { 0, "K_Y_MAX_PHY", &mex_mt_int, &config_store.Y_MAX_PHY, 0, mex_mt_obj_read_only},
    { 0, "K_X_MAX_STRIDE", &mex_mt_int, &config_store.X_MAX_STRIDE, 0, mex_mt_obj_read_only},

    MEX_CMD_REF("resize", &cmd_resize),

#else

  { 0, "X_PHY_DIM", &mex_mt_int, &config_store.x_phy_dim, 0, mex_mt_obj_read_only},
  { 0, "Y_PHY_DIM", &mex_mt_int, &config_store.y_phy_dim, 0, mex_mt_obj_read_only},

  { 0, "X_ABS_OFF", &mex_mt_int, &config_store.x_abs_offset, 0, mex_mt_obj_read_only},
  { 0, "Y_ABS_OFF", &mex_mt_int, &config_store.y_abs_offset, 0, mex_mt_obj_read_only},

  { 0, "X_ABS_DIM", &mex_mt_int, &config_store.x_abs_dim, 0, mex_mt_obj_read_only},
  { 0, "Y_ABS_DIM", &mex_mt_int, &config_store.y_abs_dim, 0, mex_mt_obj_read_only},
  { 0, "X_ABS_CEN", &mex_mt_int, &config_store.x_abs_centre, 0, mex_mt_obj_read_only},
  { 0, "Y_ABS_CEN", &mex_mt_int, &config_store.y_abs_centre, 0, mex_mt_obj_read_only},
  { 0, "X_MOV_OFF", &mex_mt_int, &config_store.x_mov_offset, 0, mex_mt_obj_read_only},
  { 0, "Y_MOV_OFF", &mex_mt_int, &config_store.y_mov_offset, 0, mex_mt_obj_read_only},
  { 0, "X_MOV_CEN", &mex_mt_int, &config_store.x_mov_centre, 0, mex_mt_obj_read_only},
  { 0, "Y_MOV_CEN", &mex_mt_int, &config_store.y_mov_center, 0, mex_mt_obj_read_only},
  { 0, "X_MOV_DIM", &mex_mt_int, &config_store.x_mov_dim, 0, mex_mt_obj_read_only},
  { 0, "Y_MOV_DIM", &mex_mt_int, &config_store.y_mov_dim, 0, mex_mt_obj_read_only},

  { 0, "Y_ZERO_RNG", &mex_mt_int, &config_store.y_zero_rng, 0, mex_mt_obj_read_only},
  { 0, "Y_ZRNG_PPI", &mex_mt_int, &config_store.y_zero_rng_ppi, 0, mex_mt_obj_read_only},
#endif
  //MEX_HOTCMD_REF("ppixy", &cmd_ppixy),


  MEX_TABLE_END
};

static const mex_mt_struct_t cfg_mt=
{{"cfg", 0, &mex_mt_struct_methods}, gc_cfg_lists};

static mex_mt_object_t cfg_obj={
  0, "cfg", &MEX_MT_CAST(cfg_mt), 0
};

#if 0
#include "tgt_generic.h"
extern tgt_list_set_t list_set[4];

static const mex_mt_object_t tgt_lists[]=
{

  { 0, "used", &mex_mt_int, (void*)(gc_mex_cpp_offsetof(tgt_list_set_t, list)+gc_mex_cpp_offsetof(tgt_list_t, items))},
  { 0, "free", &mex_mt_int, (void*)(gc_mex_cpp_offsetof(tgt_list_set_t, free_list)+gc_mex_cpp_offsetof(tgt_list_t, items))},

  MEX_TABLE_END
};

static const mex_mt_struct_t tgt_list_mt=
{{"tlst", 0, &mex_mt_struct_methods}, tgt_lists};

static mex_mt_object_t tlst_obj[4]=
{
  {0, "l0", &MEX_MT_CAST(tgt_list_mt), &list_set[0]},
  {0, "l1", &MEX_MT_CAST(tgt_list_mt), &list_set[1]},
  {0, "l2", &MEX_MT_CAST(tgt_list_mt), &list_set[2]},
  {0, "l3", &MEX_MT_CAST(tgt_list_mt), &list_set[3]},
};

#endif

static void Hello()
{
}

static mex_mt_object_t mex_gco_str=
  { 0, "gc", &mex_mt_list, 0 };

mex_mt_list_t* gc_mex_obj_root()
{
  return (mex_mt_list_t*)&mex_gco_str;
}

void gc_mex_root_register(mex_mt_object_t* const obj)
{
  mex_object_register(gc_mex_obj_root(), obj);
}

GcOpaqueObjectList* gcListOfListes;

static const mex_mt_t* findMetatype(const void* x)
{
  static const struct { const mex_mt_t* mt; const void* a;} tab[]=
  {
    {&MEX_MT_CAST(GcObjMonFriend_GcObject::gco_mt), &GcObject::GcObject_cookie_},
    {&MEX_MT_CAST(rs_mt), &GcReferenceSystem::GcReferenceSystem_cookie_},
    {&MEX_MT_CAST(GcObjMonFriend_GcGraphicItem::gci_mt), &GcGraphicItem::GcGraphicItem_cookie_},

    {0,0}
  };
  for(int i=0; tab[i].mt; ++i)
  {
    if (tab[i].a==x)
      return tab[i].mt;
  }
  return &MEX_MT_CAST(GcObjMonFriend_GcObject::gco_mt);
}

void gc_monitor_initialize()
{
  //mex_initialize();

  mex_object_register(mex_object_root(), &mex_gco_str);

  gc_mex_root_register(&cfg_obj);
#if 0
  mex_object_register(gc_mex_obj_root(), &tlst_obj[0]);
  mex_object_register(gc_mex_obj_root(), &tlst_obj[1]);
  mex_object_register(gc_mex_obj_root(), &tlst_obj[2]);
  mex_object_register(gc_mex_obj_root(), &tlst_obj[3]);
#endif
  for(GcOpaqueObjectList* ll=gcListOfListes; ll; ll=ll->gol_next_lst)
  {
    mex_mt_object_t* obj=new mex_mt_object_t;
    memset(obj, 0, sizeof *obj);
    obj->name=ll->gol_name;
    obj->metatype=&mex_mt_list;
    for(GcObject* o=ll->gol_first; o; o=o->next())
    {
      mex_mt_object_t* x=new mex_mt_object_t;
      mex_object_build(0, x, o->objName(), o, findMetatype(o->mexMetatype()), 0, 0); //o->mexMetatype(), 0, 0);
      mex_object_register((mex_mt_list_t*)obj, x);
    }
    gc_mex_root_register(obj);
  }
}

void gc_monitor()
{
  /*
  mex_object_register(mex_object_root(), &mex_gco_str);

  gc_mex_root_register(&cfg_obj);

  mex_object_register(gc_mex_obj_root(), &tlst_obj[0]);
  mex_object_register(gc_mex_obj_root(), &tlst_obj[1]);
  mex_object_register(gc_mex_obj_root(), &tlst_obj[2]);
  mex_object_register(gc_mex_obj_root(), &tlst_obj[3]);

  mex_initialize();
  */
  mex_term_set_style_render(mex_rtg_term_set_style_color);
  mex_style_enable(1);
  for(;;)
  {
    Hello();
    mex_shell();
  }
}


void gc_obj_mon_metaregister(const GcObject*)
{
}
