unit uOrdenes;

interface

uses


{$IFDEF WINDOWS}
{$IFDEF FPC-LCL}
  LResources,
{$ENDIF}

  Windows, Messages,
{$ELSE}
  unettopos, uwinmsgs, uconstantes_nettopos,
{$ENDIF}
  // uAuxiliares,
  SysUtils, Classes;

const
  maxNParams = 10;

type
  TArrayOfString = array of string;

function interpretarOrden(s: ShortString): TStringList;

{################## Ordenes ##################}
procedure AyudaAyuda(res: TStringList);

procedure orden_echo(params: TArrayOfString; res: TStringList);
procedure ayuda_echo(res: TStringList);

procedure orden_quit(params: TArrayOfString; res: TStringList);
procedure ayuda_quit(res: TStringList);

{################## Funciones Utilitarias ##################}
//Checkea que la orden comience con + y finalice con -. Si la apertura y cierre
//estan bien formados los quita de la orden. Si no tiran la excepcion correspondiente
procedure checkAperturaYCierre(var orden: string);
//Separa una linea en encabezado y parametros. Los parametros van separados por un
//espacio en blanco
procedure parsearOrden(orden: string; var encabezado: string;
  var ayuda: boolean; var params: TArrayOfString);

//Checkean la cantidad de parametros de las funciones y retornan mensajes de
//error unificados. Si hay un error tiran la excepcion correspondiente
procedure checkNParams(encabezadoOrden: string; const params: TArrayOfString;
  nParams: integer); overload;
procedure checkNParams(encabezadoOrden: string; const params: TArrayOfString;
  minNParams, maxNParams: integer); overload;

implementation

uses
  utoposock;

const
  Separadores = [' ', ',', ';', #9, #10, #13];



{ saca la primer palabra del string s}
function NextPal(var s: string): string;
var
  k1, k2: integer;
  ts: string;
  c: char;
  comilla_abierta: char;
  j1, j2: integer;

begin
  k1 := 1;
  while (k1 <= Length(s)) and (s[k1] in Separadores) do
    Inc(k1);

  j1:= k1;
  if ( k1 <= length( s ) ) then c:= s[k1];
  if ( c = '''' ) or ( c = '"' ) then
  begin
    comilla_abierta:= c;
    if k1 < length( s ) then inc( j1 );
  end
  else
    comilla_abierta:= '_';

  k2 := j1;
  j2:= -1;

  while (k2 <= Length(s)) and (
        ( comilla_abierta = c )
        or (( comilla_abierta <> c ) and not (s[k2] in Separadores) )) do
  begin
    if k2 > k1 then
       if (s[k2] = c) and ( s[k2-1] <> '\' ) then
       begin
          comilla_abierta:= '_'; // cierra comilla
          j2:= k2;
       end;
    Inc(k2);
  end;
  if j2 < 0 then j2:= k2;
  ts := copy(s, j1, j2 - j1);
  Delete(s, 1, k2);
  Result := ts;
end;


function interpretarOrden(s: ShortString): TStringList;
var
  ordenAsString: string;
  encabOrden: string;
  ayuda: boolean;
  params: TArrayOfString;
  res: TStringList;
begin
  ordenAsString := s;
  res := TStringList.Create;
  try
    checkAperturaYCierre(ordenAsString);
    parsearOrden(ordenAsString, encabOrden, ayuda, params);

    if encabOrden = '' then
      AyudaAyuda(res)
    else if encabOrden = 'echo' then
      if ayuda then
        ayuda_echo(res)
      else
        orden_echo(params, res)

    else if encabOrden = 'quit' then
      if ayuda then
        ayuda_quit(res)
      else
        orden_quit(params, res)

    {else if encabOrden = '...' then
      if ayuda then ayuda...(res)
      else orden...(params, res)}
    else
      raise Exception.Create('+ERROR: Orden desconocida ' +
        encabOrden + '. Para ver una lista de las ordenes conocidas utilice el comando ?');
  except
    On e: Exception do
    begin
      res.Clear;
      res.Add(e.Message);
    end;
  end;
  Result := res;
end;

{################## Ordenes ##################}

procedure AyudaAyuda(res: TStringList);
begin
  res.Add('****************************************');
  res.Add('* Netopos  ----------------------------*');
  res.Add('La sintaxis general es:');
  res.Add('1) Solo se interpretan lineas que comienzan con + y terminan con -,');
  res.Add(' es decir que las líneas que no comienzan con el caracter +');
  res.Add(' y terminan con el caracter - son ignoradas ');
  res.Add('2) Toda orden comienza con el signo + y termina con un - ');
  res.Add('   lo que viene despues del signo de - es ignorado.');
  res.Add('3) El conjunto de ordenes validas es: ');
  res.Add('  Echo ');
  res.Add('4) Para obtener ayuda utilizar la orden ? ');
  res.Add('  La sintaxis de la orden ? es: ');
  res.Add(' +? [orden] (enter)- ');
  res.Add('5) Para obtener ayuda general (este texto) utilizar la orden ?');
  res.Add('  sin especificar una orden');
  res.Add('');
  res.Add('Como respuesta a toda orden se contesta con la misma sintaxis');
  res.Add('siendo las respuestas posibles; ');
  res.Add(' +OK respuesta (numeral o enter)-   y ');
  res.Add(' +ERROR [<llamada> <estado>] (numeral o enter)-   ');
  res.Add('****************************************');
end;



procedure orden_echo(params: TArrayOfString; res: TStringList);
begin
  checkNParams('echo', params, 1);
  res.Add(params[0]);
end;

procedure ayuda_echo(res: TStringList);
begin
  res.Add('****************************************');
  res.Add('*                 echo                 *');
  res.Add('========================================');
  res.Add('Sintaxis:');
  res.Add('+echo String-');
  res.Add('');
  res.Add('........................................');
  res.Add('Responde un mensaje con el contenido de String.');
  res.Add('****************************************');
end;

procedure orden_quit(params: TArrayOfString; res: TStringList);
begin
{$IFDEF LINUX}
  //        xPostMessage( toposock.idTopo, WM_QUIT, 0, 0 );
  xPostMessage(toposock.idTopo, WM_CLOSE, 0, 0);
{$ELSE}
  PostMessage(toposock.Handle, WM_CLOSE, 0, 0);
{$ENDIF}

  res.add('quit...');
end;

procedure ayuda_quit(res: TStringList);
begin
  res.Add('****************************************');
  res.Add('*                 quit                 *');
  res.Add('========================================');
  res.Add('Sintaxis:');
  res.Add('+quit-');
  res.Add('');
  res.Add('........................................');
  res.Add('Cierra el topo.');
  res.Add('****************************************');
end;


{################## Funciones Utilitarias ##################}

procedure checkAperturaYCierre(var orden: string);
var
  Error: string;
begin
  Error := '';
  if orden[1] <> '+' then
    Error := 'Falta + de apertura. ';
  if orden[length(orden)] <> '-' then
    Error := Error + 'Falta - de cierre';
  if Error <> '' then
    raise Exception.Create('+ERROR: ' + Error)
  else
    orden := Copy(orden, 2, length(orden) - 2);
end;

procedure parsearOrden(orden: string; var encabezado: string;
  var ayuda: boolean; var params: TArrayOfString);
var
  nParams: integer;
begin
  encabezado := lowerCase(NextPal(orden));
  if encabezado[1] = '?' then
  begin
    ayuda := True;
    Delete(encabezado, 1, 1);
    params := nil;
  end
  else
  begin
    ayuda := False;
    if orden <> '' then
    begin
      SetLength(params, maxNParams);
      nParams := 0;
      while orden <> '' do
      begin
        params[nParams] := NextPal(orden);
        nParams := nParams + 1;
      end;
      if nParams < maxNParams then
        params := copy(params, 0, nParams);
    end
    else
      params := nil;
  end;
end;

procedure checkNParams(encabezadoOrden: string; const params: TArrayOfString;
  nParams: integer);
begin
  if Length(params) <> nParams then
  begin
    if nParams = 1 then
      raise Exception.Create('+ERROR: Error en la cantidad de parametros. ' +
        encabezadoOrden + ' recibe 1 parametro.')
    else
      raise Exception.Create('+ERROR: Error en la cantidad de parametros. ' +
        encabezadoOrden + ' recibe ' + IntToStr(nParams) +
        'parametros.');
  end;
end;

procedure checkNParams(encabezadoOrden: string; const params: TArrayOfString;
  minNParams, maxNParams: integer);
begin
  if (Length(params) < minNParams) or (Length(params) > maxNParams) then
  begin
    raise Exception.Create('+ERROR: Error en la cantidad de parametros. ' +
      encabezadoOrden + ' recibe entre' + IntToStr(minNParams) +
      ' y ' + IntToStr(maxNParams) + 'parametros.');
  end;
end;

end.
