
#include "json.hpp"
#include "io.hpp"
#include "my_assert.hpp"
#include "cutout.hpp"
#include "cutout_nljson.hpp"

#include <string>
using namespace std;

using json = nlohmann::json;


NLOHMANN_JSON_SERIALIZE_ENUM( area, {
      {area::CIRCLE, "CIRCLE"},
      {area::RECT, "RECT"},
      {area::RECT, "RANGE"},
      {area::POLYGON, "POLYGON"}
      });

NLOHMANN_JSON_SERIALIZE_ENUM( skysystem, {
      {skysystem::NONE, "NONE"},
      {skysystem::GALACTIC, "GALACTIC"},
      {skysystem::ICRS, "ICRS"}});

NLOHMANN_JSON_SERIALIZE_ENUM( specsystem, {
      {specsystem::NONE, "NONE"},
      {specsystem::VELO_LSRK, "VELO_LSRK"}, // Local Standard of Rest, Kinematic
      {specsystem::WAVE_Barycentric, "WAVE_Barycentric"},
      });






void from_json(const json& j, circle& p)
{
   j.at("lon").get_to(p.lon);
   j.at("lat").get_to(p.lat);
   j.at("radius").get_to(p.radius);
}
void from_json(const json& j, range& p)
{
   j.at("lon1").get_to(p.lon1);
   j.at("lon2").get_to(p.lon2);
   j.at("lat1").get_to(p.lat1);
   j.at("lat2").get_to(p.lat2);
}
void from_json(const json& j, polygon& p)
{
   j.at("lon").get_to(p.lon);
   j.at("lat").get_to(p.lat);
}

void from_json(const json& j, position& p)
{
   j.at("system").get_to(p.sys);

   if(j.contains("circle"))
   {
      j.at("circle").get_to(p.circ);
      p.shape = area::CIRCLE;
   }
   if(j.contains("range"))
   {
      j.at("range").get_to(p.rng);
      p.shape = area::RANGE;
   }
   if(j.contains("polygon"))
   {
      j.at("polygon").get_to(p.poly);
      p.shape = area::POLYGON;
   }
}


void from_json(const json& j, band& p)
{
   j.at("system").get_to(p.sys);
   j.at("interval").get_to(p.band_value);
}

void from_json(const json& j, time_axis& p)
{
   j.at("system").get_to(p.sys);
   j.at("interval").get_to(p.time_value);
}















void to_json(json& j, const fits_card& p)
{
   j = json{
      {"key", p.key},
         {"value", p.value},
         {"comment", p.comment}
   };
}

void from_json(const json& j, fits_card& p)
{
   string empty_str;
   if(j.contains("key")) j.at("key").get_to(p.key); else p.key = empty_str;
   if(j.contains("value")) j.at("value").get_to(p.value); else p.value = empty_str;
   if(j.contains("comment")) j.at("comment").get_to(p.comment); else p.comment = empty_str;
}


void from_json(const json& j, uint_bounds& p)
{
   if(j.contains("pix1")) j.at("pix1").get_to(p.pix1); else p.pix1 = 0;
   if(j.contains("pix2")) j.at("pix2").get_to(p.pix2); else p.pix2 = 0;
   if(j.contains("type")) j.at("type").get_to(p.type); else p.type = ' ';
}



// cutout

void to_json(json& j, const nullvals_count_s& p)
{
   j = json{
      {"fillratio", p.fill_ratio},
         {"nullcount", p.null_count},
         {"totalcount", p.total_count}
   };
}


void to_json(json& j, const cutout_res_s& p)
{
   j = json{
      {"filesize", p.filesize},
         {"filename", p.filename},
         {"nullvals_count", p.nullvals_count}
   };
}






void to_json(json& j, const coordinates& p)
{
   switch(p.shape)
   {
      case area::RANGE:
         my_assert(false, __FILE__, __LINE__, "area::RANGE no valid for struct coordinates");
         break;
      case area::POLYGON:
         my_assert(false, __FILE__, __LINE__, "area::POLYGON no valid for struct coordinates");
         break;

      case area::CIRCLE:

         // double value bit-equal e.g. was assigned not calculated dlat := dlon
         my_assert( (p.dlon_deg == p.dlat_deg) ,
               __FILE__,__LINE__,"coordinate dlon and dlat must be equal when shape is CIRCLE however "
               + to_string(p.dlon_deg) +" vs " + to_string(p.dlat_deg) );

         if(p.specsys == specsystem::NONE)
         {
            j = json{
               {"skysystem", p.skysys},
                  {"l",p.lon_deg},
                  {"b",p.lat_deg},
                  {"shape",p.shape},
                  {"r",p.dlon_deg}
            };
         }
         else
         {
            j = json{
               {"skysystem", p.skysys},
                  {"l",p.lon_deg},
                  {"b",p.lat_deg},
                  {"shape",p.shape},
                  {"r",p.dlon_deg},
                  {"specsystem",p.specsys},
                  {"vl",p.vl_kmps},
                  {"vu",p.vu_kmps}
            };
         }

         break;

      case area::RECT: 
         if(p.specsys == specsystem::NONE)
         {
            j = json{
               {"skysystem", p.skysys},
                  {"l",p.lon_deg},
                  {"b",p.lat_deg},
                  {"shape",p.shape},
                  {"dl",p.dlon_deg},
                  {"db",p.dlat_deg}
            };
         }
         else
         {
            j = json{
               {"skysystem", p.skysys},
                  {"l",p.lon_deg},
                  {"b",p.lat_deg},
                  {"shape",p.shape},
                  {"dl",p.dlon_deg},
                  {"db",p.dlat_deg},
                  {"specsystem",p.specsys},
                  {"vl",p.vl_kmps},
                  {"vu",p.vu_kmps}
            };
         }
         break;
   }
}


void from_json(const json& j, coordinates& p)
{
   if(j.contains("skysystem"))
      j.at("skysystem").get_to(p.skysys);
   else
      p.skysys = skysystem::GALACTIC;


   j.at("l").get_to(p.lon_deg);
   j.at("b").get_to(p.lat_deg);

   string shape_str;

   j.at("shape").get_to(shape_str);

   if(shape_str.compare("CIRCLE") == 0)
   {
      j.at("r").get_to(p.dlon_deg);
      p.dlon_deg = 2.0 * p.dlon_deg;
      p.dlat_deg = p.dlon_deg;
      p.shape = area::CIRCLE;
   }
   else if(shape_str.compare("RECT") == 0)
   {
      j.at("dl").get_to(p.dlon_deg);
      j.at("db").get_to(p.dlat_deg);
      p.shape = area::RECT;
   }
   else if(shape_str.compare("POLYGON") == 0)
   {
      j.at("lon").get_to(p.p_lon_deg);
      j.at("lat").get_to(p.p_lat_deg);
      p.shape = area::POLYGON;
   }
   else
   {
      my_assert( false, __FILE__,__LINE__, " unknown shape in JSON: " + shape_str );
   }

   if(j.contains("vl") || j.contains("vu"))
   {
      j.at("vl").get_to(p.vl_kmps);
      j.at("vu").get_to(p.vu_kmps);

      if(j.contains("specsystem"))
         j.at("specsystem").get_to(p.specsys);
      else
         p.specsys = specsystem::VELO_LSRK;
   }
   else
   {
      p.specsys = specsystem::NONE;
   }
}

