 /**
 * @file
 * @brief cun4sis - z/OS unicode conversion service information
 *                  NOTE: as is only buildable to 64Bit binary
 * @author limes datentechnik gmbh
 * @date  18.02.2022
 * @copyright limes datentechnik gmbh
 ******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "cunhc.h"

void printStringAsHex(char* title, char* bufferIn, int bufferlenIn)
{
   printf(title);
   for(int i=0; i < bufferlenIn; i++)
      printf("%2.2X", *(bufferIn+i));
   printf("\n");
}

static void zus_hdl_init(CUN4BIPR* uniInfo)
{
   size_t len = sizeof(CUN4BIPR) + CUN4BIPR_DDA_REQ2_ + CUN4BIPR_SUBCCSIDS_INFO_LEN_REQ;
   memset(uniInfo, '\x00', len);
   uniInfo->Version = CUN4BIPR_Ver2;
   uniInfo->Length = CUN4BIPR_LEN;
   uniInfo->DDA_Buf_Len = CUN4BIPR_DDA_REQ2_;
   uniInfo->DDA_Buf_Ptr = (void*)(uniInfo + 1);
   uniInfo->CCSID1_subCCSIDs_Info_Ptr = (void*)((char*)uniInfo->DDA_Buf_Ptr + uniInfo->DDA_Buf_Len);
}

int zus_conv_support(int from, int to, void* info)
{
   CUN4BIPR* uniInfo = (CUN4BIPR*)info;
   zus_hdl_init(uniInfo);
   uniInfo->CCSID1 = from;
   uniInfo->CCSID2 = to;
   uniInfo->Gen_Flags_In.Get_Tech_Supp_fCCSID2_tCCSID1 = 1;
   CUN4LINF(uniInfo);
   if (uniInfo->RC_RS.Return_Code || uniInfo->RC_RS.Reason_Code) {
      printf(" > RC = %d \n",uniInfo->RC_RS.Return_Code);
      printf(" > RS = %d \n",uniInfo->RC_RS.Reason_Code);
      return 0;
   }
   if (uniInfo->Gen_Flags_Out.Conversion_Supported) {
      return 1;
   }
   return 0;
}

int zus_isSupported(int ccsid, void* info)
{
   CUN4BIPR* uniInfo = (CUN4BIPR*)info;
   zus_hdl_init(uniInfo);
   uniInfo->CCSID1 = ccsid;
   uniInfo->CCSID2 = 0;
   CUN4LINF(uniInfo);
   if (uniInfo->RC_RS.Return_Code || uniInfo->RC_RS.Reason_Code) {
      printf(" > RC = %d \n",uniInfo->RC_RS.Return_Code);
      printf(" > RS = %d \n",uniInfo->RC_RS.Reason_Code);
      return 0;
   }
   if (uniInfo->Gen_Flags_Out.CCSID1_Supported) {
      return 1;
   }
   return 0;
}

void* zus_alloc_info(void)
{
   size_t len = sizeof(CUN4BIPR) + CUN4BIPR_DDA_REQ2_ + CUN4BIPR_SUBCCSIDS_INFO_LEN_REQ;
   CUN4BIPR* uniInfo = malloc(len);
   if (NULL == uniInfo)
      return NULL;
   zus_hdl_init(uniInfo);
   return (void*)uniInfo;
}

void zus_print_id1_info(void* info)
{
   CUN4BIPR* uniInfo = (CUN4BIPR*)info;
   printf(" > CCSID1 Block info .... \n");
   printf("  >> CCSID1 %d is Supported ...\n", uniInfo->CCSID1);
   printf("  >> ES...............: %X \n", uniInfo->CCSID1_ES.CCSID1_ES_ID);
   printf("  >> ES Name .........: %s \n", uniInfo->CCSID1_ES.CCSID1_ES_Name);
   printf("  >> ES Size min......: %X \n", uniInfo->CCSID1_ES_Size.CCSID1_ES_Size_Min);
   printf("  >> ES Size MAX......: %X \n", uniInfo->CCSID1_ES_Size.CCSID1_ES_Size_Max);
   printf("  >> CCSID1 Descrip...: %s \n", uniInfo->CCSID1_Description);
   printf("  >> # Subs SBCS......: %X \n", uniInfo->CCSID1_Num_Subs.CCSID1_Num_Subs_SBCS);
   printf("  >> # Subs DBCS......: %X \n", uniInfo->CCSID1_Num_Subs.CCSID1_Num_Subs_DBCS);
   printf("  >> # Subs TBCS......: %X \n", uniInfo->CCSID1_Num_Subs.CCSID1_Num_Subs_TBCS);
   printf("  >> # Subs QBCS......: %X \n", uniInfo->CCSID1_Num_Subs.CCSID1_Num_Subs_QBCS);
   printStringAsHex("  >> Subs SBCS 1 .....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_SBCS.CCSID1_Sub_Char_SBCS_1, 1);
   printStringAsHex("  >>  Subs SBCS 2 ....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_SBCS.CCSID1_Sub_Char_SBCS_2, 1);
   printStringAsHex("  >> Subs DBCS 1 .....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_DBCS.CCSID1_Sub_Char_DBCS_1, 2);
   printStringAsHex("  >>  Subs DBCS 2 ....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_DBCS.CCSID1_Sub_Char_DBCS_2, 2);
   printStringAsHex("  >> Subs TBCS 1 .....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_TBCS.CCSID1_Sub_Char_TBCS_1, 3);
   printStringAsHex("  >>  Subs TBCS 2 ....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_TBCS.CCSID1_Sub_Char_TBCS_2, 3);
   printStringAsHex("  >> Subs QBCS 1 .....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_QBCS.CCSID1_Sub_Char_QBCS_1, 4);
   printStringAsHex("  >>  Subs QBCS 2 ....: ", uniInfo->CCSID1_Sub_Char.CCSID1_Sub_Char_QBCS.CCSID1_Sub_Char_QBCS_2, 4);
                        /* SubCCSIds Info */
   printf("  >> Info Ptr ........: %4.8X \n", uniInfo->CCSID1_subCCSIDs_Info_Ptr);
   printf("  >> Info Alet .......: %4.8X \n", uniInfo->CCSID1_subCCSIDs_Info_ALET);
   printf("  >> Info # subCCSIDs.: %2.2X \n", uniInfo->CCSID1_subCCSIDs_Info_Num);

   CUN4BIPR_subCCSIDs_Info* subCCSIDsBuff = uniInfo->CCSID1_subCCSIDs_Info_Ptr;
   for (unsigned char n ='\x00'; n < uniInfo->CCSID1_subCCSIDs_Info_Num; n++) {
      printf("  >>> Sub CCSIDs Information Block # %2.2X \n",(n+1));
      printf("  >>> Sub CCSID.......: %d \n", subCCSIDsBuff->subCCSIDs_CCSID);
      printf("  >>> Sub CCSID Max C.: %2.2X \n", subCCSIDsBuff->subCCSIDs_Size);
      printf("  >>> Sub CCSID Desc..: %s \n", subCCSIDsBuff->subCCSIDs_Description);
      subCCSIDsBuff++;
   }
}

void zus_print_id2_info(void* info)
{
   CUN4BIPR* uniInfo = (CUN4BIPR*)info;
   printf(" > CCSID2 Block info .... \n");
   printf("  >> CCSID2 %d is Supported ...\n", uniInfo->CCSID2);
   printf("  >> ES...............: %X \n", uniInfo->CCSID2_ES.CCSID2_ES_ID);
   printf("  >> ES Name .........: %s \n", uniInfo->CCSID2_ES.CCSID2_ES_Name);
   printf("  >> ES Size min......: %X \n", uniInfo->CCSID2_ES_Size.CCSID2_ES_Size_Min);
   printf("  >> ES Size MAX......: %X \n", uniInfo->CCSID2_ES_Size.CCSID2_ES_Size_Max);
   printf("  >> CCSID2 Descrip...: %s \n", uniInfo->CCSID2_Description);
   printf("  >> # Subs SBCS......: %X \n", uniInfo->CCSID2_Num_Subs.CCSID2_Num_Subs_SBCS);
   printf("  >> # Subs DBCS......: %X \n", uniInfo->CCSID2_Num_Subs.CCSID2_Num_Subs_DBCS);
   printf("  >> # Subs TBCS......: %X \n", uniInfo->CCSID2_Num_Subs.CCSID2_Num_Subs_TBCS);
   printf("  >> # Subs QBCS......: %X \n", uniInfo->CCSID2_Num_Subs.CCSID2_Num_Subs_QBCS);
   printStringAsHex("  >> Subs SBCS 1 .....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_SBCS.CCSID2_Sub_Char_SBCS_1, 1);
   printStringAsHex("  >>  Subs SBCS 2 ....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_SBCS.CCSID2_Sub_Char_SBCS_2, 1);
   printStringAsHex("  >> Subs DBCS 1 .....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_DBCS.CCSID2_Sub_Char_DBCS_1, 2);
   printStringAsHex("  >>  Subs DBCS 2 ....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_DBCS.CCSID2_Sub_Char_DBCS_2, 2);
   printStringAsHex("  >> Subs TBCS 1 .....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_TBCS.CCSID2_Sub_Char_TBCS_1, 3);
   printStringAsHex("  >>  Subs TBCS 2 ....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_TBCS.CCSID2_Sub_Char_TBCS_2, 3);
   printStringAsHex("  >> Subs QBCS 1 .....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_QBCS.CCSID2_Sub_Char_QBCS_1, 4);
   printStringAsHex("  >>  Subs QBCS 2 ....: ", uniInfo->CCSID2_Sub_Char.CCSID2_Sub_Char_QBCS.CCSID2_Sub_Char_QBCS_2, 4);
                        /* SubCCSIds Info */
   printf("  >> Info Ptr ........: %4.8X \n", uniInfo->CCSID2_subCCSIDs_Info_Ptr);
   printf("  >> Info Alet .......: %4.8X \n", uniInfo->CCSID2_subCCSIDs_Info_ALET);
   printf("  >> Info # subCCSIDs.: %2.2X \n", uniInfo->CCSID2_subCCSIDs_Info_Num);

   CUN4BIPR_subCCSIDs_Info* subCCSIDsBuff = uniInfo->CCSID2_subCCSIDs_Info_Ptr;
   for (unsigned char n ='\x00'; n < uniInfo->CCSID2_subCCSIDs_Info_Num; n++) {
      printf("  >>> Sub CCSIDs Information Block # %2.2X \n",(n+1));
      printf("  >>> Sub CCSID.......: %d \n", subCCSIDsBuff->subCCSIDs_CCSID);
      printf("  >>> Sub CCSID Max C.: %2.2X \n", subCCSIDsBuff->subCCSIDs_Size);
      printf("  >>> Sub CCSID Desc..: %s \n", subCCSIDsBuff->subCCSIDs_Description);
      subCCSIDsBuff++;
   }
}

void zus_print_conv_info(void* info)
{
   CUN4BIPR* uniInfo = (CUN4BIPR*)info;
   int id1 = uniInfo->CCSID1;
   int id2 = uniInfo->CCSID2;
   printf("type of conversion: (%d) %s\n", uniInfo->Conversion_Type, (uniInfo->Conversion_Type == 1) ? "direct" : (uniInfo->Conversion_Type == 2) ? "indirect" : "unknown");
   printf("technics for conversion from %05d to %05d: %.8s\n", id1, id2, uniInfo->Conv_Tech_fCCSID1_tCCSID2);
/*   printStringAsHex("Conv_Tech_fCCSID1_tCCSID2: ", uniInfo->Conv_Tech_fCCSID1_tCCSID2, 8); */
   printf("technics for conversion from %05d to %05d: %.8s\n", id2, id1, uniInfo->Conv_Tech_fCCSID2_tCCSID1);
/*   printStringAsHex("Conv_Tech_fCCSID2_tCCSID1: ", uniInfo->Conv_Tech_fCCSID2_tCCSID1, 8); */
   printf("CTLF entries: %d, %d\n", uniInfo->CCSID1_CTLDEF_Num, uniInfo->CCSID2_CTLDEF_Num);
}

int main(int argc, char * argv[])
{
   int argindex = 1;
   int verbose = 0;
   if (NULL != argv[1] && strncmp(argv[1], "-v", 2) == 0) {
      verbose = 1;
      argindex = 2;
   }
   if (verbose && argc < 3) {
      printf("provide up to 2 CCSIDs on the command line.\n");
   }
   int id1 = (NULL != argv[argindex]) ? atoi(argv[argindex]) : 0;
   int id2 = (NULL != argv[argindex+1]) ? atoi(argv[argindex+1]) : 0;
   if (id1 == 0) {
      printf("please provide at least 1 CCSID.\n");
      return 0;
   }
   if (verbose)
      printf("Starting Character Information Sample program... \n");
   void* hdl = zus_alloc_info();
   if (NULL == hdl) {
      printf("failed to alloc handle.\n");
      return 1;
   }

   if (id2 == 0 || (!verbose && argc > 3)) {
      for (int i=argindex; i < argc ; i++) {
         id1 = atoi(argv[i]);
         if (zus_isSupported(id1, hdl)) {
            printf("  >> CCSID %05d is supported\n", id1);
            if (verbose) {
               zus_print_id1_info(hdl);
            }
         } else {
            printf("  >> CCSID %05d is NOT Supported\n", id1);
         }
      }
   } else {
      if (zus_conv_support(id1, id2, hdl)) {
         printf("conversion from %05d to %05d is supported\n", id1, id2);
         if (verbose) {
            zus_print_id1_info(hdl);
            zus_print_id2_info(hdl);
         }
         zus_print_conv_info(hdl);
      } else {
         printf("conversion from %05d to %05d is NOT supported\n", id1, id2);
      }
   }
   free(hdl);
   return 0;
}
