/*=========================================================================

  Program: GDCM (Grassroots DICOM). A DICOM library

  Copyright (c) 2006-2011 Mathieu Malaterre
  All rights reserved.
  See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "gdcmJSON.h"
#include "gdcmGlobal.h"
#include "gdcmDicts.h"
#include "gdcmBase64.h"
#include "gdcmSystem.h"

#ifdef GDCM_USE_SYSTEM_JSON
#include <json.h>
#endif

#ifdef GDCM_HAVE_JSON_OBJECT_OBJECT_GET_EX
// https://github.com/json-c/json-c/issues/142
static inline json_object * json_object_object_get_old(json_object * obj, const char * name) {
    json_object * sub;
    return json_object_object_get_ex(obj, name, & sub) ? sub : NULL;
}
#else
#define json_object_object_get_old json_object_object_get
#endif

/*
 * Implementation is done based on Sup166, which may change in the future.
 */

// TODO: CP 246 / VR=SQ + Sequence: !

// Clarification needed:
//  "00081070":{
//    "Tag":"00081070",
//    "VR":"PN",
//    "Value":[
//      null
//    ]
//  },


// Need to pay attention that:
// \ISO 2022 IR 13\ISO 2022 IR 87
// encodes to:
// [ null, "ISO 2022 IR 13", "ISO 2022 IR 87" ]
// or ?
// [ "", "ISO 2022 IR 13", "ISO 2022 IR 87" ]

// TODO:
// DS/IS should be encoded as number

// Question, does F.2.5 DICOM JSON Model Null Values
// imply:
// "Sequence": [ null ] ?

// does not make any sense to send Group Length...

// Clarification needed, specs says IS should be a Number but example uses
// String

namespace gdcm
{

#ifdef GDCM_USE_SYSTEM_JSON
static inline void wstrim(std::string& str)
{
  str.erase(0, str.find_first_not_of(' '));
  str.erase(str.find_last_not_of(' ')+1);
}

static inline bool CanContainBackslash( const VR::VRType vrtype )
{
  gdcm_assert( VR::IsASCII( vrtype ) );
  // PS 3.5-2011 / Table 6.2-1 DICOM VALUE REPRESENTATIONS
  switch( vrtype )
    {
  case VR::AE: // ScheduledStationAETitle
    //case VR::AS: // no
    //case VR::AT: // binary
  case VR::CS: // SpecificCharacterSet
  case VR::DA: // CalibrationDate
  case VR::DS: // FrameTimeVector
  case VR::DT: // ReferencedDateTime
    //case VR::FD: // binary
    //case VR::FL:
  case VR::IS: // ReferencedFrameNumber
  case VR::LO: // OtherPatientIDs
    //case VR::LT: // VM1
    //case VR::OB: // binary
    //case VR::OD: // binary
    //case VR::OF: // binary
    //case VR::OW: // binary
  case VR::PN: // PerformingPhysicianName
  case VR::SH: // PatientTelephoneNumbers
    //case VR::SL: // binary
    //case VR::SQ: // binary
    //case VR::SS: // binary
    //case VR::ST: // VM1
  case VR::TM: // CalibrationTime
  case VR::UI: // SOPClassesInStudy
    //case VR::UL: // binary
    //case VR::UN: // binary
    //case VR::US: // binary
    //case VR::UT: // VM1
    gdcm_assert( !(vrtype & VR::VR_VM1) );
    return true;
  default:
    ;
    }
  return false;
}
#endif

class JSONInternal
{
public:
  JSONInternal()= default;
  bool PrettyPrint{false};
  bool PreferKeyword{false};
};

JSON::JSON()
{
  Internals = new JSONInternal;
}

JSON::~JSON()
{
  delete Internals;
}

void JSON::SetPrettyPrint(bool onoff)
{
  Internals->PrettyPrint = onoff;
}
bool JSON::GetPrettyPrint() const
{
  return Internals->PrettyPrint;
}
void JSON::PrettyPrintOn()
{
  Internals->PrettyPrint = true;
}
void JSON::PrettyPrintOff()
{
  Internals->PrettyPrint = false;
}

#ifdef GDCM_USE_SYSTEM_JSON

/*
  "StudyInstanceUID": {
    "Tag": "0020000D",
    "VR": "UI",
    "Value": [ "1.2.392.200036.9116.2.2.2.1762893313.1029997326.945873" ]
    },
*/

static void DataElementToJSONArray( const VR::VRType vr, const DataElement & de, json_object *my_array )
{
  if( de.IsEmpty() )
    {
    // F.2.5 DICOM JSON Model Null Values
    if( vr == VR::PN )
      {
      json_object *my_object_comp = json_object_new_object();
      json_object_array_add(my_array, my_object_comp );
      }
    //else
    //  json_object_array_add(my_array, NULL );
    return;
    }
  // else
  gdcm_assert( !de.IsEmpty() );
  const bool checkbackslash = CanContainBackslash( vr );
  const ByteValue * bv = de.GetByteValue();
  const char * value = bv->GetPointer();
  size_t len = bv->GetLength();

  if( vr == VR::UI )
    {
    const std::string strui( value, len );
    const size_t lenuid = strlen( strui.c_str() ); // trick to remove trailing \0
    json_object_array_add(my_array, json_object_new_string_len(strui.c_str(), lenuid));
    }
  else if( vr == VR::PN )
    {
    const char *str1 = value;
    // remove whitespace:
    while( str1[len-1] == ' ' )
      {
      len--;
      }
    gdcm_assert( str1 );
    std::stringstream ss;
    static const char *Keys[] = {
      "Alphabetic",
      "Ideographic",
      "Phonetic",
    };
    while (1)
      {
      gdcm_assert( str1 && (size_t)(str1 - value) <= len );
      const char * sep = strchr(str1, '\\');
      const size_t llen = (sep != NULL) ? (sep - str1) : (value + len - str1);
      const std::string component(str1, llen);

      const char *str2 = component.c_str();
      gdcm_assert( str2 );
      const size_t len2 = component.size();
      gdcm_assert( len2 == llen );

      int idx = 0;
      json_object *my_object_comp = json_object_new_object();
      while (1)
        {
        gdcm_assert( str2 && (size_t)(str2 - component.c_str() ) <= len2 );
        const char * sep2 = strchr(str2, '=');
        const size_t llen2 = (sep2 != NULL) ? (sep2 - str2) : (component.c_str() + len2 - str2);
        const std::string group(str2, llen2);
        const char *thekey = Keys[idx++];

        json_object_object_add(my_object_comp, thekey,
          json_object_new_string_len( group.c_str(), group.size() ) );
        if (sep2 == NULL) break;
        str2 = sep2 + 1;
        }
      json_object_array_add(my_array, my_object_comp);
      if (sep == NULL) break;
      str1 = sep + 1;
      gdcm_assert( checkbackslash );
      }
    }
  else if( vr == VR::DS || vr == VR::IS )
    {
    const char *str1 = value;
    gdcm_assert( str1 );
    VRToType<VR::IS>::Type vris;
    VRToType<VR::DS>::Type vrds;
    while (1)
      {
      std::stringstream ss;
      gdcm_assert( str1 && (size_t)(str1 - value) <= len );
      const char * sep = strchr(str1, '\\');
      const size_t llen = (sep != NULL) ? (sep - str1) : (value + len - str1);
      // This is complex, IS/DS should not be stored as string anymore
      switch( vr )
        {
      case VR::IS:
        ss.str( std::string(str1, llen) );
        ss >> vris;
        json_object_array_add(my_array, json_object_new_int(vris)); // json_object_new_int takes an int32_t
        break;
      case VR::DS:
        ss.str( std::string(str1, llen) );
        ss >> vrds;
        json_object_array_add(my_array, json_object_new_double(vrds));
        break;
      default:
        gdcm_assert( 0 ); // programmer error
        }
      if (sep == NULL) break;
      str1 = sep + 1;
      gdcm_assert( checkbackslash );
      }
    }
  else if( checkbackslash )
    {
    const char *str1 = value;
    gdcm_assert( str1 );
    while (1)
      {
      gdcm_assert( str1 && (size_t)(str1 - value) <= len );
      const char * sep = strchr(str1, '\\');
      const size_t llen = (sep != NULL) ? (sep - str1) : (value + len - str1);
      json_object_array_add(my_array, json_object_new_string_len(str1, llen));
      if (sep == NULL) break;
      str1 = sep + 1;
      }
    }
  else // default
    {
    json_object_array_add(my_array, json_object_new_string_len(value, len));
    }
}

// Encode from DICOM to JSON
// TODO: do I really need libjson for this task ?
// FIXME: once again everything is loaded into memory
static void ProcessNestedDataSet( const DataSet & ds, json_object *my_object, const bool preferkeyword )
{
  const Global& g = GlobalInstance;
  const Dicts &dicts = g.GetDicts();
  const Dict &d = dicts.GetPublicDict(); (void)d;

  std::vector<char> buffer;
  for( DataSet::ConstIterator it = ds.Begin();
    it != ds.End(); ++it )
    {
    const DataElement &de = *it;
    VR::VRType vr = de.GetVR();
    const Tag& t = de.GetTag();
    if( t.IsGroupLength() ) continue; // I do not see why we should send those ?
    std::string strowner;
    const char *owner = 0;
    if( t.IsPrivate() && !t.IsPrivateCreator() )
      {
      strowner = ds.GetPrivateCreator(t);
      owner = strowner.c_str();
      }
    const DictEntry &entry = dicts.GetDictEntry(t,owner);
    const std::string & str_tag = t.PrintAsContinuousUpperCaseString();
    const bool issequence = vr == VR::SQ || de.IsUndefinedLength();
    const bool isprivatecreator = t.IsPrivateCreator();
    if( issequence ) vr = VR::SQ;
    else if( isprivatecreator ) vr = VR::LO; // always prefer VR::LO (over invalid/UN)
    else if( vr == VR::INVALID ) vr = VR::UN;
    const char * vr_str = VR::GetVRString(vr);
    gdcm_assert( VR::GetVRTypeFromFile(vr_str) != VR::INVALID );

    json_object *my_object_cur;
    my_object_cur = json_object_new_object();
    json_object *my_array;
    my_array = json_object_new_array();

    //json_object_object_add(my_object_cur, "Tag",
    //  json_object_new_string( str_tag.c_str()) );
    json_object_object_add(my_object_cur, "VR",
      json_object_new_string_len( vr_str, 2 ) );
    if( owner )
      {
      json_object_object_add(my_object_cur, "PrivateCreator",
        json_object_new_string( owner ) );
      }

    if( vr == VR::SQ )
      {
      SmartPointer<SequenceOfItems> sqi;
      sqi = de.GetValueAsSQ();
      if( sqi )
        {
        int nitems = sqi->GetNumberOfItems();
        for(int i = 1; i <= nitems; ++i)
          {
          const Item &item = sqi->GetItem( i );
          const DataSet &nested = item.GetNestedDataSet();

          json_object *my_object_sq;
          my_object_sq = json_object_new_object();

          ProcessNestedDataSet( nested, my_object_sq, preferkeyword );
          json_object_array_add(my_array, my_object_sq );
          }
        }
      else if( const SequenceOfFragments *sqf = de.GetSequenceOfFragments() )
        {
        json_object_array_add(my_array, NULL ); // FIXME
        gdcm_assert( 0 );
        }
      else
        {
        gdcm_assert( de.IsEmpty() );
        //json_object_array_add(my_array, NULL ); // F.2.5 req ?
        }
      //json_object_object_add(my_object_cur, "Sequence", my_array );
      int numel = json_object_array_length ( my_array );
      if( numel )
        json_object_object_add(my_object_cur, "Value", my_array );
      }
    else if( VR::IsASCII( vr ) )
      {
      DataElementToJSONArray( vr, de, my_array );
      int numel = json_object_array_length ( my_array );
      if( numel )
        {
        if( vr == VR::PN )
          {
          //json_object_object_add(my_object_cur, "PersonName", my_array );
          json_object_object_add(my_object_cur, "Value", my_array );
          }
        else
          json_object_object_add(my_object_cur, "Value", my_array );
        }
      }
    else
      {
      const char *wheretostore = "Value";
      switch( vr )
        {
      case VR::FD:
          {
          // Does not work, see https://github.com/json-c/json-c/pull/59
          Element<VR::FD,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            json_object_array_add(my_array, json_object_new_double( el.GetValue( i ) ));
            }
          }
        break;
      case VR::FL:
          {
          Element<VR::FL,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            json_object_array_add(my_array, json_object_new_double( el.GetValue( i ) ));
            }
          }
        break;
      case VR::SS:
          {
          Element<VR::SS,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            json_object_array_add(my_array, json_object_new_int( el.GetValue( i ) ));
            }
          }
        break;
      case VR::US:
          {
          Element<VR::US,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            json_object_array_add(my_array, json_object_new_int( el.GetValue( i ) ));
            }
          }
        break;
      case VR::SL:
          {
          Element<VR::SL,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            json_object_array_add(my_array, json_object_new_int( el.GetValue( i ) ));
            }
          }
        break;
      case VR::UL:
          {
          Element<VR::UL,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            json_object_array_add(my_array, json_object_new_int( el.GetValue( i ) ));
            }
          }
        break;
      case VR::AT:
          {
          Element<VR::AT,VM::VM1_n> el;
          el.Set( de.GetValue() );
          int ellen = el.GetLength();
          for( int i = 0; i < ellen; ++i )
            {
            const std::string atstr = el.GetValue( i ).PrintAsContinuousUpperCaseString();
            json_object_array_add(my_array, json_object_new_string( atstr.c_str() ));
            }
          }
        break;
      case VR::UN:
      case VR::INVALID:
      case VR::OD:
      case VR::OF:
      case VR::OB:
      case VR::OW:
          {
          gdcm_assert( !de.IsUndefinedLength() ); // handled before
          const ByteValue * bv = de.GetByteValue();
          wheretostore = "InlineBinary";
          if( bv )
            {
            const char *src = bv->GetPointer();
            const size_t len = bv->GetLength();
            gdcm_assert( len % 2 == 0 );
            const size_t len64 = Base64::GetEncodeLength(src, len);
            buffer.resize( len64 );
            const size_t ret = Base64::Encode( &buffer[0], len64, src, len );
            gdcm_assert( ret != 0 );
            json_object_array_add(my_array, json_object_new_string_len(&buffer[0], len64));
            }
          }
        break;
      default:
        gdcm_assert( 0 ); // programmer error
        }
      json_object_object_add(my_object_cur, wheretostore, my_array );
      }
    //const char *keyword = entry.GetKeyword();
    //gdcm_assert( keyword && *keyword );
    //if( preferkeyword && keyword && *keyword && !t.IsPrivateCreator() )
    //  {
    //  json_object_object_add(my_object, keyword, my_object_cur );
    //  }
    //else
      {
      json_object_object_add(my_object, str_tag.c_str(), my_object_cur );
      }
    }
}
#endif

bool JSON::Code(DataSet const & ds, std::ostream & os)
{
#ifdef GDCM_USE_SYSTEM_JSON
  json_object *my_object;
  my_object = json_object_new_object();

  ProcessNestedDataSet( ds, my_object, Internals->PreferKeyword );

  const char* str = NULL;
  if( Internals->PrettyPrint )
    {
#ifdef JSON_C_VERSION
    str = json_object_to_json_string_ext(my_object, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY );
#else
    str = json_object_to_json_string( my_object );
#endif
    }
  else
    {
    str = json_object_to_json_string( my_object );
    }
  os << str;
  json_object_put(my_object); // free memory
  return true;
#else
  (void)ds;
  (void)os;
  return false;
#endif
}

#ifdef GDCM_USE_SYSTEM_JSON
// Paranoid
static inline bool CheckTagKeywordConsistency( const char *name, const Tag & thetag )
{
  // Can be keyword or tag
  gdcm_assert( name );

  // start with easy one:
  // only test first string character:
  bool istag = *name >= '0' && *name <= '9'; // should be relatively efficient
  if( istag )
    {
    gdcm_assert( strlen(name) == 8 );
    Tag t;
    t.ReadFromContinuousString( name );
    return t == thetag;
    }
  // else keyword:
  const Global& g = GlobalInstance;
  const Dicts &dicts = g.GetDicts();
  const Dict &d = dicts.GetPublicDict();
  const char * keyword = d.GetKeywordFromTag(thetag);
  if( !keyword )
    {
    gdcmDebugMacro( "Unknown Keyword: " << name );
    return true;
    }
  // else
  gdcm_assert( strcmp( name, keyword ) == 0 );
  return strcmp( name, keyword ) == 0;
}
#endif

#ifdef GDCM_USE_SYSTEM_JSON
#ifdef JSON_C_VERSION
static void ProcessJSONElement( const char *tag_str, json_object * obj, DataElement & de )
{
  json_type jtype = json_object_get_type( obj );
  gdcm_assert( jtype == json_type_object );
  json_object * jvr = json_object_object_get_old(obj, "VR");

  const char * vr_str = json_object_get_string ( jvr );
  de.GetTag().ReadFromContinuousString( tag_str );
  const char * pc_str = 0;
  if( de.GetTag().IsPrivate() && !de.GetTag().IsPrivateCreator() )
    {
    json_object * jprivatecreator = json_object_object_get_old(obj, "PrivateCreator");
    pc_str = json_object_get_string ( jprivatecreator );
    gdcm_assert( pc_str );
    }

  VR::VRType vrtype = VR::GetVRTypeFromFile( vr_str );
  gdcm_assert( vrtype != VR::INVALID );
  gdcm_assert( vrtype != VR::VR_END );
  de.SetVR( vrtype );

  if( vrtype == VR::SQ )
    {
    json_object * jvalue = json_object_object_get_old(obj, "Value");
    json_type jvaluetype = json_object_get_type( jvalue );
    gdcm_assert( jvaluetype != json_type_null && jvaluetype == json_type_array  );
#ifndef NDEBUG
    json_object * jseq = json_object_object_get_old(obj, "Sequence");
    json_type jsqtype = json_object_get_type( jseq );
    gdcm_assert( jsqtype == json_type_null );
#endif
    if( jvaluetype == json_type_array )
      {
      // Create a Sequence
      SmartPointer<SequenceOfItems> sq = new SequenceOfItems;
      sq->SetLengthToUndefined();

      int sqlen = json_object_array_length ( jvalue );
      for( int itemidx = 0; itemidx < sqlen; ++itemidx )
        {
        json_object * jitem = json_object_array_get_idx ( jvalue, itemidx );
        json_type jitemtype = json_object_get_type( jitem );
        gdcm_assert( jitemtype == json_type_object );
        //const char * dummy = json_object_to_json_string ( jitem );

        // Create an item
        Item item;
        item.SetVLToUndefined();
        DataSet &nds = item.GetNestedDataSet();

#ifdef JSON_C_VERSION
        json_object_iterator it;
        json_object_iterator itEnd;
        it = json_object_iter_begin(jitem);
        itEnd = json_object_iter_end(jitem);

        while (!json_object_iter_equal(&it, &itEnd))
          {
          const char *name = json_object_iter_peek_name(&it);
          gdcm_assert( name );
          json_object * value = json_object_iter_peek_value (&it);
          DataElement lde;
          ProcessJSONElement( name, value, lde );
          nds.Insert( lde );
          json_object_iter_next(&it);
          }
#endif
        sq->AddItem(item);
        }

      // Insert sequence into data set
      de.SetValue(*sq);
      de.SetVLToUndefined();
      }
    }
  else if( VR::IsASCII( vrtype ) )
    {
/*
    F.2.5              DICOM JSON Model Null Values
    If an attribute is present in DICOM but empty, it shall be preserved in the DICOM JSON object and passed
    with the value of "null". For example:
    "Value": [ null ]
*/
    json_object * jvalue = json_object_object_get_old(obj, "Value");
#ifndef NDEBUG
    json_object * jpn = json_object_object_get_old(obj, "PersonName");
    json_type jpntype = json_object_get_type( jpn );
    gdcm_assert( jpntype == json_type_null );
#endif
    json_type jvaluetype = json_object_get_type( jvalue );
    //const char * dummy = json_object_to_json_string ( jvalue );
    gdcm_assert( jvaluetype == json_type_null || jvaluetype == json_type_array );
    if( jvaluetype == json_type_array )
      {
      //gdcm_assert( vrtype != VR::PN );
      const int valuelen = json_object_array_length ( jvalue );
      std::string str;
      for( int validx = 0; validx < valuelen; ++validx )
        {
        if( validx ) str += '\\';
        json_object * value = json_object_array_get_idx ( jvalue, validx );
        json_type valuetype = json_object_get_type( value );
        if( value )
          {
          gdcm_assert( valuetype != json_type_null );
          std::string value_str;
          std::stringstream ss;
          VRToType<VR::IS>::Type vris;
          VRToType<VR::DS>::Type vrds;
          switch( vrtype )
            {
          case VR::PN:
              {
              json_object * jopn[3];
              jopn[0] = json_object_object_get_old(value, "Alphabetic");
              jopn[1]= json_object_object_get_old(value, "Ideographic");
              jopn[2]= json_object_object_get_old(value, "Phonetic");
              for( int i = 0; i < 3; ++i )
                {
                const char *tmp = json_object_get_string ( jopn[i] );
                if( tmp )
                  {
                  if( i ) value_str += '=';
                  value_str += tmp;
                  }
                }
              }
            break;
          case VR::IS:
            vris = json_object_get_int( value );
            ss << vris;
            value_str = ss.str();
            break;
          case VR::DS:
            vrds = json_object_get_double( value );
            ss << vrds;
            value_str = ss.str();
            break;
          default:
            value_str = json_object_get_string ( value );
            }
          str += value_str;
          }
        else
          {
          // We have a [ null ] array, so at most there is a single item:
          gdcm_assert( valuelen == 1 );
          gdcm_assert( valuetype == json_type_null );
          }
        }
      if( str.size() % 2 )
        {
        if( vrtype == VR::UI )
          str.push_back( 0 );
        else
          str.push_back( ' ' );
        }
      de.SetByteValue( &str[0], str.size() );
      }
#ifndef NDEBUG
    else if( jpntype == json_type_array )
      {
      gdcm_assert( 0 );
      }
#endif
    }
  else
    {
    json_object * jvaluebin = json_object_object_get_old(obj, "InlineBinary");
    json_type jvaluebintype = json_object_get_type( jvaluebin );
    json_object * jvalue = json_object_object_get_old(obj, "Value");
    json_type jvaluetype = json_object_get_type( jvalue );
    //const char * dummy = json_object_to_json_string ( jvalue );
    gdcm_assert( jvaluetype == json_type_array || jvaluetype == json_type_null );
    if( jvaluetype == json_type_array )
      {
      DataElement locde;
      const int valuelen = json_object_array_length ( jvalue );
      const int vrsizeof = vrtype == VR::INVALID ? 0 : de.GetVR().GetSizeof();
      switch( vrtype )
        {
      case VR::FD:
          {
          Element<VR::FD,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_double );
            const double v = json_object_get_double ( value );
            el.SetValue(v, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      case VR::FL:
          {
          Element<VR::FL,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_double );
            const double v = json_object_get_double ( value );
            el.SetValue(v, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      case VR::SS:
          {
          Element<VR::SS,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_int );
            const int v = json_object_get_int( value );
            el.SetValue(v, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      case VR::US:
          {
          Element<VR::US,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_int );
            const int v = json_object_get_int( value );
            el.SetValue(v, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      case VR::SL:
          {
          Element<VR::SL,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_int );
            const int v = json_object_get_int( value );
            el.SetValue(v, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      case VR::UL:
          {
          Element<VR::UL,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_int );
            const int v = json_object_get_int( value );
            el.SetValue(v, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      case VR::AT:
          {
          Element<VR::AT,VM::VM1_n> el;
          el.SetLength( valuelen * vrsizeof );
          for( int validx = 0; validx < valuelen; ++validx )
            {
            json_object * value = json_object_array_get_idx ( jvalue, validx );
            gdcm_assert( json_object_get_type( value ) == json_type_string );
            const char *atstr = json_object_get_string( value );
            Tag t;
            t.ReadFromContinuousString( atstr );
            el.SetValue(t, validx);
            }
          locde = el.GetAsDataElement();
          }
        break;
      default:
        gdcm_assert( 0 );
        }
      if( !locde.IsEmpty() )
        de.SetValue( locde.GetValue() );
      }
    else if( jvaluebintype == json_type_array )
      {
      DataElement locde;
      const int valuelen = json_object_array_length ( jvaluebin );
      switch( vrtype )
        {
      case VR::UN:
      case VR::INVALID:
      case VR::OB:
      case VR::OD:
      case VR::OF:
      case VR::OW:
          {
          gdcm_assert( valuelen == 1 || valuelen == 0 );
          if( valuelen )
            {
            json_object * value = json_object_array_get_idx ( jvaluebin, 0 );
            json_type valuetype = json_object_get_type( value );
            if( value )
              {
              gdcm_assert( valuetype != json_type_null );
              const char * value_str = json_object_get_string ( value );
              gdcm_assert( value_str );
              const size_t len64 = strlen( value_str );
              const size_t len = Base64::GetDecodeLength( value_str, len64 );
              std::vector<char> buffer;
              buffer.resize( len );
              const size_t ret = Base64::Decode( &buffer[0], len,
                value_str, len64 );
              gdcm_assert( ret != 0 );
              locde.SetByteValue( &buffer[0], len );
              }
            else
              {
              // We have a [ null ] array, so at most there is a single item:
              gdcm_assert( valuelen == 1 );
              gdcm_assert( valuetype == json_type_null );
              }
            }
          }
        break;
      default:
        gdcm_assert( 0 );
        }
      if( !locde.IsEmpty() )
        de.SetValue( locde.GetValue() );
      }
    else
      {
      gdcm_assert( jvaluebintype == json_type_null && jvaluetype == json_type_null );
      }
    }
}
#endif
#endif

bool JSON::Decode(std::istream & is, DataSet & ds)
{
#ifdef GDCM_USE_SYSTEM_JSON

#ifdef JSON_C_VERSION
  json_object *jobj = NULL;
  const char *mystring = NULL;
  int stringlen = 0;
  enum json_tokener_error jerr;
  std::string str;
  json_tokener * tok = json_tokener_new ();
  do
    {
    std::getline( is, str );
    mystring = str.c_str();
    stringlen = str.size();
    jobj = json_tokener_parse_ex(tok, mystring, stringlen);
    //if( is.eof() ) break;
    } while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue );

  if (jerr != json_tokener_success)
    {
    fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr));
    // Handle errors, as appropriate for your application.
    gdcm_assert( 0 );
    }
  if (tok->char_offset < stringlen) // XXX shouldn't access internal fields
    {
    // Handle extra characters after parsed object as desired.
    // e.g. issue an error, parse another object from that point, etc...
    }
  // Success, use jobj here.
  json_tokener_free( tok );
#else
  std::stringstream ss;
  std::string str;
  while( std::getline( is, str ) )
    {
    ss << str;
    }
  const std::string & wholestr = ss.str();
  json_object *obj;
  obj = json_tokener_parse( wholestr.c_str() );
#endif

#ifdef JSON_C_VERSION
  json_object_iterator it;
  json_object_iterator itEnd;
  it = json_object_iter_begin(jobj);
  itEnd = json_object_iter_end(jobj);

  while (!json_object_iter_equal(&it, &itEnd))
    {
    const char *name = json_object_iter_peek_name(&it);
    gdcm_assert( name );
    json_object * value = json_object_iter_peek_value (&it);
    DataElement de;
    ProcessJSONElement( name, value, de );
    ds.Insert( de );
    json_object_iter_next(&it);
    }
  return true;
#else
  gdcmErrorMacro( "Version too old" );
  return false;
#endif
#else
  (void)is;
  (void)ds;
  return false;
#endif
}

} // end namespace gdcm
