// ---------------------------------------------------------------------------
// - JsonMime.cpp                                                            -
// - afnix:wax service - json mime 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 "Vector.hpp"
#include "Utility.hpp"
#include "Rsamples.hpp"
#include "JsonMime.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "OutputFile.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // the json content type
  static const String JSWR_MIME_TYPE = "application/json"; 

  // the rsamples data array constants
  static const String JSWR_RSPL_MODE = "\"mode\"";
  static const String JSWR_RSPL_HIDX = "\"hidx\"";
  static const String JSWR_RSPL_TIME = "\"time\"";
  static const String JSWR_RSPL_DATA = "\"data\"";
  static const String JSWR_RSPL_NAME = "\"RSAMPLES\"";

  // this procedure writes a rsamples object to a buffer
  static void json_write (const Rsamples& data, Buffer& buf) {
    // collect name
    String name = data.getname ();
    // collect data elements
    long rows = data.getrows ();
    long cols = data.getcols ();
    long rmax = rows - 1L;
    long cmax = cols - 1L;
    bool sflg = data.getsflg ();
    long psiz = data.getpsiz ();
    // the line to write
    String line;
    // write the name first if any
    line = "{"; line+= eolc;
    if (name.isnil () == false) {
      line = name; line+= " = {"; line+= eolc;
    }
    buf.add (line);
    // data mode
    line = "  "; line+= JSWR_RSPL_MODE; line += " : "; line+= JSWR_RSPL_NAME;
    line+= ',';  line+= eolc; 
    buf.add (line);
    // data index
    line = "  "; line+= JSWR_RSPL_HIDX; line += " : "; line+= "null";
    line+= ',';  line+= eolc; 
    buf.add (line);
    // time data
    line = "  "; line+= JSWR_RSPL_TIME; line+= " : ";
    buf.add (line);
    if (data.stamped () == true) {
      line = "["; line += eolc;
      buf.add (line);
      for (long k = 0; k < rows; k++) {
	t_real t = data.gettime (k);
	String s = Utility::tostring (t, psiz, sflg);
	line = "    "; line+= s;
	if (k < rmax) line+= ",";
	line+= eolc;
	buf.add (line);
      }
      line = "  ],"; line+= eolc;
      buf.add (line);
    } else {
      line = "null,"; line+= eolc;
      buf.add (line);
    }
    // data array
    line = "  \"data\" : ["; line+= eolc;
    buf.add (line);
    for (long j = 0; j < cols; j++) {
      line = "    ["; line+= eolc;
      buf.add (line);
      for (long k = 0; k < rows; k++) {
	t_real v = data.get (k, j);
	String s = Utility::tostring (v, psiz, sflg);
	line = "      "; line += s;
	if (k < rmax) line+= ",";
	line+= eolc;
	buf.add (line);
      }
      if (j < cmax) 
	line = "    ],";
      else
	line = "    ]";
      line += eolc;
      buf.add (line);
    }
    line = "  ]"; line+= eolc;
    buf.add (line);
    // close the name
    line = "}"; line+= eolc;
    buf.add (line);
  }

  // this procedure writes a rsamples object to an output stream
  static void json_write (const Rsamples& data, OutputStream& os) {
    // collect name and info
    String name = data.getname ();
    String info = data.getinfo ();
    // check for valid name and stram
    if (name.isnil () == true) {
      throw Exception ("json-error", "invalid nil name for mime writer");
    }
    // collect data elements
    long rows = data.getrows ();
    long cols = data.getcols ();
    long rmax = rows - 1L;
    long cmax = cols - 1L;
    bool sflg = data.getsflg ();
    long psiz = data.getpsiz ();
    // the line to write
    String line;
    // write the name first if any
    line = "{";
    if (name.isnil () == false) {
      line = name; line+= " = {";
    }
    os.writeln (line);
    // data mode
    line = "  "; line +=JSWR_RSPL_MODE; line += " : "; line+= JSWR_RSPL_NAME;
    line+= ',';
    os.writeln (line);
    // data index
    line = "  "; line +=JSWR_RSPL_HIDX; line += " : "; line+= "null";
    line+= ',';
    os.writeln (line);
    // time data
    line = "  "; line+= JSWR_RSPL_TIME; line+= " : ";
    os.write (line);
    if (data.stamped () == true) {
      os.writeln ("[");
      for (long k = 0; k < rows; k++) {
	t_real t = data.gettime (k);
	String s = Utility::tostring (t, psiz, sflg);
	line = "    "; line+= s;
	if (k < rmax) line+= ",";
	os.writeln (line);
      }
      os.writeln ("  ],");
    } else {
      os.writeln ("null,");
    }
    // data array
    os.writeln ("  \"data\" : [");
    for (long j = 0; j < cols; j++) {
      os.writeln ("    [");
      for (long k = 0; k < rows; k++) {
	t_real v = data.get (k, j);
	String s = Utility::tostring (v, psiz, sflg);
	line = "      "; line += s;
	if (k < rmax) line+= ",";
	os.writeln (line);
      }
      if (j < cmax) 
	os.writeln ("    ],");
      else
	os.writeln ("    ]");
    }
    os.writeln ("  ]");
    // close the name
    os.writeln ("}");
  }

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a default json mime

  JsonMime::JsonMime (void) {
    d_mime = JSWR_MIME_TYPE;
    p_mobj = nullptr;
  }

  // create a json mime by object

  JsonMime::JsonMime (Object* mobj) {
    d_mime = JSWR_MIME_TYPE;
    Object::iref (p_mobj = mobj);
  }

  // destroy this writer

  JsonMime::~JsonMime (void) {
    Object::dref (p_mobj);
  }

  // return the object class name

  String JsonMime::repr (void) const {
    return "JsonMime";
  }

  // write the data samples to a buffer

  void JsonMime::write (Buffer& buf) const {
    rdlock ();
    try {
      // check for rsamples object
      Rsamples* data = dynamic_cast <Rsamples*> (p_mobj);
      if (data != nullptr) json_write (*data, buf);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // write the data samples to an output stream

  void JsonMime::write (OutputStream& os) const {
    rdlock ();
    try {
      // check for rsamples object
      Rsamples* data = dynamic_cast <Rsamples*> (p_mobj);
      if (data != nullptr) json_write (*data, os);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the mime object

  Object* JsonMime::getobj (void) const {
    rdlock ();
    try {
      Object* result = p_mobj;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // the quark zone
  static const long QUARK_ZONE_LENGTH = 1;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);

  // the object supported quarks
  static const long QUARK_GETOBJ = zone.intern ("get-object");

  // create a new object in a generic way

  Object* JsonMime::mknew (Vector* argv) {
    long argc = (argv == nullptr) ? 0 : argv->length ();
    // create a default html tree object
    if (argc == 0) return new JsonMime;
    // check for 1 argument
    if (argc == 1) {
      Object* obj = argv->get (0);
      return new JsonMime (obj);
    }
    throw Exception ("argument-error",
                     "too many argument with json mime constructor");
  }
  
  // return true if the given quark is defined

  bool JsonMime::isquark (const long quark, const bool hflg) const {
    rdlock ();
    try {
      if (zone.exists (quark) == true) {
	unlock ();
	return true;
      } 
      bool result = hflg ? Mime::isquark (quark, hflg) : false;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // apply this object with a set of arguments and a quark

  Object* JsonMime::apply (Runnable* robj, Nameset* nset, const long quark,
			     Vector* argv) {
    // get the number of arguments
    long argc = (argv == nullptr) ? 0 : argv->length ();
    
    // dispatch 0 argument
    if (argc == 1) {
      if (quark == QUARK_GETOBJ) {
	rdlock ();
	try {
	  Object* result = getobj ();
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
    }
    // call the mime method
    return Mime::apply (robj, nset, quark, argv);
  }
}
