unit ujson;

{$mode delphi}

interface

uses
  Classes, SysUtils, fpjson,jsonparser,matreal,MatEnt,xmatdefs,uauxiliares,
  strutils,ufechas,jsonscanner,contnrs;

{La idea de esta unidad es hacer más ADME user friendly los objetos JSON
 de Pascal}

type

  TInfoObjetoJson = record
    Nombre:string;
    Tipo:string;
  end;

  TDAofInfoObjetoJson = array of TInfoObjetoJson;

  TObjetoJson = class;

  TDAOfObjetoJson = array of TObjetoJson;

 { TObjetoJson }

  TObjetoJson = class

    jObject: TJSONObject;

    constructor createFromArchi(archi: string);
    constructor createFromJsonObject(jObj: TJSONObject);
    class function createFromString(str: string): TObjetoJson;


    function getNombresYTiposHijos: TDAofInfoObjetoJson;
    function getVectR(nombreSerie:string):TVectR;
    function getVectE(nombreSerie:string):TVectE;
    function getTDAOfDateTimeFromIsoStr(nombreSerie:string):TDAOfDateTime;
    function getString(nombreCampo:string):string;
    function getFloat(nombreCampo:string):NReal;
    function getInteger(nombreCampo:string):Integer;

    function getJsonArray(nombreCampo: string): TDAOfObjetoJson;
    function getObjetoHijo(nombreCampo: string): TObjetoJson;

    procedure SaveToFile(nombreArchi:String);
    procedure Free;

  end;


{Estas funciones estan pensadas para los pronósticos de SGU, que vienen como
arrays cuyos elementos son un objeto JSON con dos elementos, "Fecha" (string en
YYYY-MM-DDTHH:MM:SS) y "Afluentes", que es un array de reales de un elemento solo}
function getStringArrayFromObjectArray(objArray:TDAOfObjetoJson;indice:Integer): TDAofString;
function getDAOfDatTimeFromObjectArray(objArray:TDAOfObjetoJson;indice:Integer): TDAOfDateTime;
function getVectRFromObjectArray(objArray:TDAOfObjetoJson;indice:Integer): TVectR;
function getMatRFromObjectArray(objArray:TDAOfObjetoJson;indice:Integer): TMatR;


implementation

function getStringArrayFromObjectArray(objArray: TDAOfObjetoJson;
  indice: Integer): TDAofString;
var
 NItems,kItem:integer;
 res:TDAOfString;
 nombre: String;
 str: string;
 jObjeto:TObjetoJson;
begin

  NItems:=Length(objArray);
  SetLength(res,NItems);

  for kItem:=0 to NItems - 1 do
  begin
    jObjeto:=objArray[kItem];
    nombre:=jObjeto.jObject.Names[indice];
    str:=(jObjeto.jObject.Elements[nombre]).AsString;
    res[kItem]:=str;
  end;
  Result:=res;
end;

function getDAOfDatTimeFromObjectArray(objArray: TDAOfObjetoJson;
  indice: Integer): TDAOfDateTime;
var
  strs:TDAofString;
  kFecha,NFechas:integer;
  res:TDAOfDateTime;
begin

  strs:=getStringArrayFromObjectArray(objArray,indice);
  NFechas:=Length(strs);
  SetLength(res,NFechas);

  for kFecha:=0 to NFechas - 1 do
    res[kFecha]:=IsoStrToDateTime(strs[kFecha]);

  SetLength(strs,0);

  Result:=res;

end;

function getVectRFromObjectArray(objArray: TDAOfObjetoJson; indice: Integer
  ): TVectR;
var
 NItems,kItem:integer;
 res:TVectR;
 nombre: String;
 vectorPeroEnRealidadMuestra: TVectR;
 jObjeto:TObjetoJson;
begin

  NItems:=Length(objArray);
  res:=TVectR.Create_Init(NItems);

  for kItem:=0 to NItems - 1 do
  begin
    jObjeto:=objArray[kItem];
    nombre:=jObjeto.jObject.Names[indice];
    vectorPeroEnRealidadMuestra:=jObjeto.getVectR(nombre);
    res.pon_e(kItem + 1, vectorPeroEnRealidadMuestra.e(1));
    vectorPeroEnRealidadMuestra.Free;
  end;
  Result:=res;
end;

function getMatRFromObjectArray(objArray: TDAOfObjetoJson; indice: Integer
  ): TMatR;
var
 NMuestras,kMuestra,NEnsembles,kEnsemble:integer;
 res:TMatR;
 nombre: String;
 vectorEnsemblesDeKMuestra: TVectR;
 jObjeto:TObjetoJson;
begin

  // solo para saber cuántos ensembles tengo
  jObjeto:=objArray[0];
  nombre:=jObjeto.jObject.Names[indice];
  vectorEnsemblesDeKMuestra:=jObjeto.getVectR(nombre);
  NEnsembles:=vectorEnsemblesDeKMuestra.n;
  vectorEnsemblesDeKMuestra.Free;
  NMuestras:=Length(objArray);

  res:=TMatR.Create_Init(NMuestras,NEnsembles);

  // las filas son las muestras, las columnas los ensembles
  for kMuestra:=0 to NMuestras - 1 do
  begin
    jObjeto:=objArray[kMuestra];
    nombre:=jObjeto.jObject.Names[indice];
    vectorEnsemblesDeKMuestra:=jObjeto.getVectR(nombre);

    for kEnsemble:=1 to NEnsembles do
      res.pon_fila(kMuestra + 1, kEnsemble, vectorEnsemblesDeKMuestra.e(kEnsemble));

    vectorEnsemblesDeKMuestra.Free;
  end;
  Result:=res;
end;

{ TObjetoJson }

constructor TObjetoJson.createFromArchi(archi: string);
var
  aStream: TFileStream;
  jParser: TJSONParser;
  jData:TJSONData;
  kElemento: Integer;
begin

  aStream:=TFileStream.Create(archi,fmOpenRead);
  jParser:=TJSONParser.Create(aStream);
  jData:=jParser.Parse;

  if jData is TJSONObject then
    jObject:= TJSONObject(jData)
  else
    raise Exception.Create('No es un TJSONObject');

  aStream.Free;
  jParser.Free;

end;

constructor TObjetoJson.createFromJsonObject(jObj: TJSONObject);
begin
  jObject:=jObj;
end;


class function TObjetoJson.createFromString(str: string):TObjetoJson;
var
  res:TObjetoJson;
  archiStrDummy:string;
begin
  archiStrDummy:='archiDummy';
  StrToArchi(archiStrDummy,str);
  res:=TObjetoJson.createFromArchi(archiStrDummy);
  DeleteFile(archiStrDummy);
  Result:=res;
end;

function TObjetoJson.getNombresYTiposHijos: TDAofInfoObjetoJson;
var
  kObjeto,NObjetos:integer;
  res:TDAofInfoObjetoJson;
begin
  NObjetos:=jObject.Count;
  SetLength(res,NObjetos);

  for kObjeto:=0 to NObjetos - 1 do
  begin
    res[kObjeto].Nombre:=jObject.Names[kObjeto];
    res[kObjeto].Tipo:=JSONTypeName(jObject.Types[jObject.Names[kObjeto]]);
  end;
  Result:=res;
end;



function TObjetoJson.getVectR(nombreSerie: string): TVectR;
var
  arr:TJSONArray;
  vec:TVectR;
  kMuestra:Integer;
  str: String;
begin
  arr:=jObject.Get(nombreSerie,arr);
  vec:=TVectR.Create_Init(arr.Count);

  for kMuestra := 0 to arr.Count - 1 do
  begin
    if not (arr[kMuestra].IsNull) then
      vec.pon_e(kMuestra + 1 ,arr.Floats[kMuestra])
    else
      vec.pon_e(kMuestra + 1 ,0);
  end;

  Result:=vec;

end;

function TObjetoJson.getVectE(nombreSerie: string): TVectE;
var
  arr:TJSONArray;
  vec:TVectE;
  kMuestra:Integer;
begin
  arr:=jObject.Get(nombreSerie,arr);
  vec:=TVectE.Create_Init(arr.Count);

  for kMuestra := 0 to arr.Count - 1 do
    vec.pon_e(kMuestra + 1 ,arr.Integers[kMuestra]);

  Result:=vec;

end;

function TObjetoJson.getTDAOfDateTimeFromIsoStr(nombreSerie: string
  ): TDAOfDateTime;
var
  arr:TJSONArray;
  str:string;
  res:TDAOfDateTime;
  kMuestra:Integer;
begin
  arr:=jObject.Get(nombreSerie,arr);
  SetLength(res,arr.Count);

  for kMuestra := 0 to arr.Count - 1 do
  begin
    str:=arr.Strings[kMuestra];
    res[kMuestra]:=IsoStrToDateTime(str);
  end;

  Result:=res;

end;

function TObjetoJson.getString(nombreCampo: string): string;
var
  str:string;
begin
  Result:=jObject.Get(nombreCampo,str);
end;

function TObjetoJson.getFloat(nombreCampo: string): NReal;
var
  flt:NReal;
begin
  Result:=jObject.Get(nombreCampo,flt);
end;

function TObjetoJson.getInteger(nombreCampo: string): Integer;
var
  intgr:Integer;
begin
  Result:=jObject.Get(nombreCampo,intgr);
end;

function TObjetoJson.getJsonArray(nombreCampo: string): TDAOfObjetoJson;
var
  arr:TJSONArray;
  res:TDAOfObjetoJson;
  NElementos,kElemento:Integer;
  jData:TJSONData;
begin


  arr:=jObject.Get(nombreCampo,arr);

  NElementos:=arr.Count;
  SetLength(res,NElementos);

  for kElemento:=0 to NElementos - 1 do
  begin
    jData:= arr.Items[kElemento].Clone;
    if jData is TJSONObject then
      res[kElemento]:= TObjetoJson.createFromJsonObject(TJSONObject(jData))
    else
      raise Exception.Create('El elemento no es un TJSONObject');
  end;

  Result:=res;

end;

function TObjetoJson.getObjetoHijo(nombreCampo: string): TObjetoJson;
var
  jObj:TJSONObject;
  res:TObjetoJson;
begin
  jObj:=jObject.Get(nombreCampo,jObj);
  Result:= TObjetoJson.createFromJsonObject(jObj);
end;



procedure TObjetoJson.SaveToFile(nombreArchi: String);
var
  str:string;
  opciones:TFormatOptions;
begin
  str:=jObject.FormatJSON;
  StrToArchi(nombreArchi,str);
end;

procedure TObjetoJson.Free;
begin
  if jObject <> nil then
    jObject.Free;
  Inherited Free;
end;

end.

