(********************************************************************)
(*                                                                  *)
(*  copy.s7i      Generate code for the copy functions.             *)
(*  Copyright (C) 1990 - 1994, 2004 - 2014  Thomas Mertes           *)
(*                                                                  *)
(*  This file is part of the Seed7 compiler.                        *)
(*                                                                  *)
(*  This program is free software; you can redistribute it and/or   *)
(*  modify it under the terms of the GNU General Public License as  *)
(*  published by the Free Software Foundation; either version 2 of  *)
(*  the License, or (at your option) any later version.             *)
(*                                                                  *)
(*  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.  See the   *)
(*  GNU General Public License for more details.                    *)
(*                                                                  *)
(*  You should have received a copy of the GNU General Public       *)
(*  License along with this program; if not, write to the           *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


var typeReferenceHash: copyFunction is typeReferenceHash.EMPTY_HASH;
var boolean_type_hash: cpy_declared is boolean_type_hash.EMPTY_HASH;
var string_type_hash: parametersOfHshCpy is string_type_hash.EMPTY_HASH;


const proc: process_cpy_declaration (in type: object_type,
    inout expr_type: c_expr) is forward;


const proc: process_cpy_call (in type: object_type,
    in string: param_a, in string: param_b, inout string: expr) is forward;


const proc: process_arr_cpy_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
    var string: select_value is "";
    var integer: arraySize is 0;
  begin
    if object_type in copyFunction then
      diagnosticLine := diagnosticLine(copyFunction[object_type]);
    end if;
    select_value := select_value_from_rtlObjectStruct(array_element[object_type]);
    process_create_declaration(array_element[object_type], c_expr);
    process_destr_declaration(array_element[object_type], c_expr);
    process_cpy_declaration(array_element[object_type], c_expr);
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void cpy_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " *a, ";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    if object_type not in array_minIdx or object_type not in array_maxIdx then
      if array_element[object_type] not in typeCategory or
          typeCategory[array_element[object_type]] not in simpleValueType then
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "memSizeType i;\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "memSizeType size;\n";
      end if;
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "memSizeType size_a = (uintType)((*a)->max_position - (*a)->min_position + 1);\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "memSizeType size_b = (uintType)(b->max_position - b->min_position + 1);\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "(*a)->min_position = b->min_position;\n";
      c_expr.expr &:= diagnosticLine;
      c_expr.expr &:= "(*a)->max_position = b->max_position;\n";
      c_expr.expr &:= diagnosticLine;
      if array_element[object_type] in typeCategory and
          typeCategory[array_element[object_type]] in simpleValueType then
        c_expr.expr &:= "if (size_a != size_b) {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "*a=(";
        c_expr.expr &:= type_name(object_type);
        c_expr.expr &:= ")(arrRealloc((arrayType)(*a), size_a, size_b));\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "memmove((*a)->arr, b->arr, size_b * sizeof(genericType));\n";
      else
        c_expr.expr &:= "if (size_a == size_b) {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "size = size_a;\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "} else {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "if (size_a < size_b) {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "*a=(";
        c_expr.expr &:= type_name(object_type);
        c_expr.expr &:= ")(arrRealloc((arrayType)(*a), size_a, size_b));\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "size = size_a;\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "for (i = size_a; i < size_b; i++) {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "(*a)->arr[i]";
        c_expr.expr &:= select_value;
        c_expr.expr &:= "=";
        process_create_call(array_element[object_type],
            "b->arr[i]" & select_value, c_expr.expr);
        c_expr.expr &:= ";\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "} else /* size_a > size_b */ {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "size = size_b;\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "for (i = size_b; i < size_a; i++) {\n";
        c_expr.expr &:= diagnosticLine;
        process_destr_call(array_element[object_type],
            "(*a)->arr[i]" & select_value, c_expr.expr);
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "*a=(";
        c_expr.expr &:= type_name(object_type);
        c_expr.expr &:= ")(arrRealloc((arrayType)(*a), size_a, size_b));\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "while (size != 0) {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "size--;\n";
        c_expr.expr &:= diagnosticLine;
        process_cpy_call(array_element[object_type],
            "(*a)->arr[size]" & select_value,
            "b->arr[size]" & select_value, c_expr.expr);
        c_expr.expr &:= ";\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
      end if;
    else
      arraySize := succ(array_maxIdx[object_type] - array_minIdx[object_type]);
      if array_element[object_type] in typeCategory and
          typeCategory[array_element[object_type]] in simpleValueType then
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "memmove((*a)->arr, b->arr, (uintType) ";
        c_expr.expr &:= integerLiteral(arraySize);
        c_expr.expr &:= " * sizeof(genericType));\n";
      else
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "memSizeType index = (uintType) ";
        c_expr.expr &:= integerLiteral(arraySize);
        c_expr.expr &:= ";\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "while (index != 0) {\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "index--;\n";
        c_expr.expr &:= diagnosticLine;
        process_cpy_call(array_element[object_type],
            "(*a)->arr[index]" & select_value,
            "b->arr[index]" & select_value, c_expr.expr);
        c_expr.expr &:= ";\n";
        c_expr.expr &:= diagnosticLine;
        c_expr.expr &:= "}\n";
      end if;
    end if;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    cpy_declared @:= [object_type] TRUE;
  end func;


const proc: process_sct_cpy_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
    var integer: structSize is 0;
    var integer: elementIndex is 0;
    var string: select_value is "";
  begin
    if object_type in copyFunction then
      diagnosticLine := diagnosticLine(copyFunction[object_type]);
    end if;
    if object_type in struct_size then
      structSize := struct_size[object_type];
    end if;
    for elementIndex range 0 to pred(structSize) do
      process_cpy_declaration(struct_element_type[object_type][elementIndex], c_expr);
    end for;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void cpy_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " a, ";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    # The usage_count is currently left unchanged for a deep copy.
    # c_expr.expr &:= "a->usage_count = b->usage_count;\n";
    # The dynamic type of a struct cannot change with a deep copy.
    # c_expr.expr &:= "a->type_num = b->type_num;\n";
    for elementIndex range 0 to pred(structSize) do
      select_value := select_value_from_rtlObjectStruct(struct_element_type[object_type][elementIndex]);
      c_expr.expr &:= diagnosticLine;
      process_cpy_call(struct_element_type[object_type][elementIndex],
          "a->stru[" & str(elementIndex) & "]" & select_value,
          "b->stru[" & str(elementIndex) & "]" & select_value, c_expr.expr);
      c_expr.expr &:= ";\n";
    end for;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    cpy_declared @:= [object_type] TRUE;
  end func;


const proc: process_hsh_cpy_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
  begin
    if object_type in copyFunction then
      diagnosticLine := diagnosticLine(copyFunction[object_type]);
    end if;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void cpy_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " *a, ";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "hshCpy(a, b";
    c_expr.expr &:= parametersOfHshCpy[object_type];
    c_expr.expr &:= ");\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    cpy_declared @:= [object_type] TRUE;
  end func;


const proc: process_itf_cpy_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  local
    var string: diagnosticLine is "";
  begin
    if object_type in copyFunction then
      diagnosticLine := diagnosticLine(copyFunction[object_type]);
    end if;
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "static void cpy_";
    c_expr.expr &:= str(typeNumber(object_type));
    c_expr.expr &:= " (";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " *a, ";
    c_expr.expr &:= type_name(object_type);
    c_expr.expr &:= " b)\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "{\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "interfaceType old_value = *a;\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "*a=b;\n";
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "if ((*a)->usage_count != 0) { (*a)->usage_count++; }\n";
    process_destr_declaration(object_type, global_c_expr);
    c_expr.expr &:= diagnosticLine;
    process_destr_call(object_type, "old_value", c_expr.expr);
    c_expr.expr &:= diagnosticLine;
    c_expr.expr &:= "}\n";
    c_expr.expr &:= noDiagnosticLine;
    c_expr.expr &:= "\n";
    cpy_declared @:= [object_type] TRUE;
  end func;


const proc: process_cpy_declaration (in type: object_type,
    inout expr_type: c_expr) is func

  begin
    if object_type not in cpy_declared then
      if object_type in typeCategory then
        case typeCategory[object_type] of
          when {ARRAYOBJECT}:
            process_arr_cpy_declaration(object_type, c_expr);
          when {STRUCTOBJECT}:
            process_sct_cpy_declaration(object_type, c_expr);
          when {HASHOBJECT}:
            process_hsh_cpy_declaration(object_type, c_expr);
          when {INTERFACEOBJECT}:
            process_itf_cpy_declaration(object_type, c_expr);
          otherwise:
            cpy_declared @:= [object_type] TRUE;
        end case;
      else
        c_expr.expr &:= "/* cpy_";
        c_expr.expr &:= str(typeNumber(object_type));
        c_expr.expr &:= " declaration for type ";
        c_expr.expr &:= type_name2(object_type);
        c_expr.expr &:= " */\n\n";
      end if;
    end if;
  end func;


const proc: process_cpy_call (in type: object_type,
    in string: param_a, in string: param_b, inout string: expr) is func

  begin
    if object_type in typeCategory then
      case typeCategory[object_type] of
        when simpleValueType:   expr &:= param_a & "=" & param_b;
        when {BIGINTOBJECT}:    expr &:= "bigCpy(&(" & param_a & "), " & param_b & ")";
        when {STRIOBJECT}:      expr &:= "strCopy(&(" & param_a & "), " & param_b & ")";
        when {BSTRIOBJECT}:     expr &:= "bstCpy(&(" & param_a & "), " & param_b & ")";
        when {FILEOBJECT}:      expr &:= "filCpy(&(" & param_a & "), " & param_b & ")";
        when {SETOBJECT}:       expr &:= "setCpy(&(" & param_a & "), " & param_b & ")";
        when {POLLOBJECT}:      expr &:= "polCpy(" & param_a & ", " & param_b & ")";
        when {REFLISTOBJECT}:   expr &:= "rflCpy(&(" & param_a & "), " & param_b & ")";
        when {WINOBJECT}:       expr &:= "drwCpy(&(" & param_a & "), " & param_b & ")";
        when {POINTLISTOBJECT}: expr &:= "bstCpy(&(" & param_a & "), " & param_b & ")";
        when {PROCESSOBJECT}:   expr &:= "pcsCpy(&(" & param_a & "), " & param_b & ")";
        when {PROGOBJECT}:      expr &:= "prgCpy(&(" & param_a & "), " & param_b & ")";
        when {DATABASEOBJECT}:  expr &:= "sqlCpyDb(&(" & param_a & "), " & param_b & ")";
        when {SQLSTMTOBJECT}:   expr &:= "sqlCpyStmt(&(" & param_a & "), " & param_b & ")";
        when {ARRAYOBJECT, HASHOBJECT, INTERFACEOBJECT}:
          expr &:= "cpy_";
          expr &:= str(typeNumber(object_type));
          expr &:= "((";
          expr &:= type_name(object_type);
          expr &:= " *)(&(";
          expr &:= param_a;
          expr &:= ")), (";
          expr &:= type_name(object_type);
          expr &:= ")(";
          expr &:= param_b;
          expr &:= "))";
        when {STRUCTOBJECT}:
          expr &:= "cpy_";
          expr &:= str(typeNumber(object_type));
          expr &:= "((";
          expr &:= type_name(object_type);
          expr &:= ")(";
          expr &:= param_a;
          expr &:= "), (";
          expr &:= type_name(object_type);
          expr &:= ")(";
          expr &:= param_b;
          expr &:= "))";
      end case;
    else
      expr &:= param_b & " /* cpy_";
      expr &:= str(typeNumber(object_type));
      expr &:= " for type ";
      expr &:= type_name2(object_type);
      expr &:= " */";
    end if;
  end func;