/*
 * nasd_edrfs_test_common.c
 *
 * Internal routines for the NASD EDRFS test library
 *
 * Author: Nat Lanza
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_error.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_edrfs_types.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_edrfs_client.h>
#include <nasd/nasd_edrfs_types_marshall.h>

#include <stdio.h>
#include <stdlib.h>

#include "nasd_edrfs_test_common.h"
#include "nasd_edrfs_test_rc.h"

nasd_status_t nasd_edrfs_test_init(nasd_edrfs_mount_info_t *minfo) {
  nasd_status_t rc;

  /* init the memory library */
  rc = nasd_mem_init();
  if (rc) {
    fprintf(stderr, "ERROR (%s:%d): cannot init NASD mem library, rc=%ld\n",
	   __FILE__, __LINE__, (long)rc);
    return(rc);
  }

  /* init the client libraries */
  rc = nasd_cl_p_init();
  if (rc) {
    fprintf(stderr, "ERROR (%s:%d): cannot init NASD client library, rc=%ld\n",
	   __FILE__, __LINE__, (long)rc);
    return(rc);
  }

  rc = nasd_edrfscli_init();
  if (rc) {
    fprintf(stderr, "ERROR (%s:%d): cannot init NASD/EDRFS client library, rc=%ld\n",
	   __FILE__, __LINE__, (long)rc);
    return(rc);
  }

  /* now bind to the server */

  DEBUG(50) printf("Binding to server %s...\n", minfo->server_name);

  rc = nasd_bind_to_edrfs_server(minfo->server_name, NASD_EDRFS_SERVER_PORT,
			       minfo->binding_type, NULL, 0,
			       &minfo->server_handle);

  if (rc) {
    fprintf(stderr, "ERROR: failed binding to server %s, rc=0x%x (%s)\n",
	    minfo->server_name, rc, nasd_error_string(rc));
    return rc;
  }

  DEBUG(50) printf("nasd_bind_to_edrfs_server returned %ld\n", (long)rc);

  return NASD_SUCCESS;
}

nasd_status_t nasd_edrfs_test_shutdown(nasd_edrfs_mount_info_t *minfo) {
  nasd_status_t rc;

  rc = nasd_unbind_edrfs_server(&minfo->server_handle);
  
  if (rc) {
    fprintf(stderr, "ERROR: failed unbinding from server %s, rc=0x%x (%s)\n",
	    minfo->server_name, rc, nasd_error_string(rc));
    return rc;
  }
  
  nasd_edrfscli_shutdown();
  nasd_cl_p_shutdown();
  nasd_mem_shutdown();

  return NASD_SUCCESS;
}

nasd_status_t nasd_edrfs_test_mount(nasd_edrfs_mount_info_t *minfo) {
  nasd_rpc_status_t op_status;
  nasd_edrfs_mount_res_t res;
  nasd_edrfs_mount_args_t args;

  strncpy(args.in_dirpath, minfo->path_name, NASD_EDRFS_MAX_NAME_LEN);

  nasd_edrfscli_mount(minfo->server_handle, &args, &res, &op_status);

  if (res.nasd_status | op_status){
    fprintf(stderr,"ERROR: mount failed, rc=0x%x (%s) op_status=0x%x\n",
	   res.nasd_status, nasd_error_string(res.nasd_status), op_status);
    return res.nasd_status;
  }

  /* XXX - Need to do a drive call and verify this information */

  return NASD_SUCCESS;
}

int nasd_edrfs_test_encode_len(int len){
  return (((len / 3) + 1) * 4);
}

static void encode_6_to_1(char *dest, nasd_byte data){
  /* Should be a table, but I don't want to make one that big */
  if(data < 12)
    *dest = (char) '.' + data; /* ./ and numbers */
  else if (data < 38)
    *dest = (char) 'A' + data - 12; /* capitial letters */
  else
    *dest = (char) 'a' + data - 38; /* lowwer case letters */
}

static void decode_1_to_6(nasd_byte *dest, char data){
  if(data <= '9')
    *dest = (nasd_byte) data - '.'; /* ./ and numbers */
  else if (data <= 'Z')
    *dest = (nasd_byte) data + 12 - 'A'; /* capitial letters */
  else
    *dest = (nasd_byte) data + 38 - 'a'; /* lowwer case letters */
}

nasd_status_t nasd_edrfs_test_encode(char **str, nasd_byte *data, int len){
  int i, trips, slen, slack;

  NASD_ASSERT(str != NULL);
  trips  = len / 3;
  slen = (trips + 1) * 4;
  slack = len - (trips * 3);

  NASD_Malloc(*str, slen, (char *));
  if(*str == NULL)
    return NASD_NO_MEM;
  memset(*str, 0, slen);

  for(i=0;i<trips;i++){
    encode_6_to_1(&(*str)[i * 4],(data[i * 3] & 0xFC) >> 2);
    encode_6_to_1(&(*str)[i * 4 + 1],((data[i * 3] & 0x3) << 4) |
		  ((data[i * 3 + 1] & 0xF0) >> 4));
    encode_6_to_1(&(*str)[i * 4 + 2],((data[i * 3 + 1] & 0x0F) << 2) |
		  ((data[i * 3 + 2] & 0xC0) >> 6));
    encode_6_to_1(&(*str)[i * 4 + 3],data[i * 3 + 2] & 0x3F);
  }
  switch (slack){
  case 0:
    break;
  case 1:
    encode_6_to_1(&(*str)[i * 4],(data[i * 3] & 0xFC) >> 2); 
    encode_6_to_1(&(*str)[i * 4 + 1],(data[i * 3] & 0x3) << 4); 
    break;
  case 2:
    encode_6_to_1(&(*str)[i * 4],(data[i * 3] & 0xFC) >> 2); 
    encode_6_to_1(&(*str)[i * 4 + 1],((data[i * 3] & 0x3) << 4) |
		  ((data[i * 3 + 1] & 0xF0) >> 4)); 
    encode_6_to_1(&(*str)[i * 4 + 2],(data[i * 3 + 1] & 0x0F) << 2);
    break;
  default:
    fprintf(stderr, "This should never happen. %s(%d)", __FILE__, __LINE__);
    return NASD_FAIL;
  }
  return NASD_SUCCESS;
}


nasd_status_t nasd_edrfs_test_decode(nasd_byte **dest, int *len, char *data){
  int i, trips, slen, slack;
  nasd_byte tmp;

  NASD_ASSERT(data != NULL);
  NASD_ASSERT(dest != NULL);
  slen = strlen(data);
  trips = slen / 4;
  slack = slen - (trips * 4);
  *len = trips * 3;
  switch (slack){
  case 0:
    break;
  case 2:
    *len += 1;
    break;
  case 3:
    *len += 2;
    break;
  default:
    fprintf(stderr, "This should never happen. %s(%d)", __FILE__, __LINE__);
    return NASD_FAIL;
  }
    
  NASD_Malloc(*dest, *len, (nasd_byte *));
  if(*dest == NULL)
    return NASD_NO_MEM;

  for(i=0;i<trips;i++){
    decode_1_to_6(&tmp,data[i * 4]);
    (*dest)[i * 3] = tmp << 2;
    decode_1_to_6(&tmp,data[i * 4 + 1]);
    (*dest)[i * 3] = (*dest)[i * 3] | (tmp >> 4);
    (*dest)[i * 3 + 1] = (tmp & 0x0F) << 4;
    decode_1_to_6(&tmp,data[i * 4 + 2]);
    (*dest)[i * 3 + 1] = (*dest)[i * 3 + 1] | (tmp >> 2);
    (*dest)[i * 3 + 2] = (tmp & 0x03) << 6;
    decode_1_to_6(&tmp,data[i * 4 + 3]);
    (*dest)[i * 3 + 2] = (*dest)[i * 3 + 2] | tmp;
  }
  switch (slack){
  case 0:
    break;
  case 2:
    decode_1_to_6(&tmp,data[i * 4]);
    (*dest)[i * 3] = tmp << 2;
    decode_1_to_6(&tmp,data[i * 4 + 1]);
    (*dest)[i * 3] = (*dest)[i * 3] | (tmp >> 4);
    break;
  case 3:
    decode_1_to_6(&tmp,data[i * 4]);
    (*dest)[i * 3] = tmp << 2;
    decode_1_to_6(&tmp,data[i * 4 + 1]);
    (*dest)[i * 3] = (*dest)[i * 3] | (tmp >> 4);
    (*dest)[i * 3 + 1] = (tmp & 0x0F) << 4;
    decode_1_to_6(&tmp,data[i * 4 + 2]);
    (*dest)[i * 3 + 1] = (*dest)[i * 3 + 1] | (tmp >> 2);
    break;
  default:
    fprintf(stderr, "This should never happen. %s(%d)", __FILE__, __LINE__);
    return NASD_FAIL;
  }
  return NASD_SUCCESS;
}

nasd_status_t nasd_edrfs_test_rc_convert(char *arg, nasd_status_t *rc) {
  int i=0;

  if(strspn(arg,"0123456789") != strlen(arg)){
    while(nasd_edrfs_err_list[i].str != NULL){
      if(strcmp(nasd_edrfs_err_list[i].str, arg) == 0){
	*rc = nasd_edrfs_err_list[i].status;
	return NASD_SUCCESS;
      }
      i++;
    }
    return NASD_FAIL;
  }
  *rc = atoi(arg);
  return NASD_SUCCESS;
}

void nasd_edrfs_test_print_attribute(const nasd_attribute_t attr){
  nasd_edrfs_attributes_t edrfsattr;
  nasd_edrfs_attributes_t_unmarshall((nasd_otw_base_t *) attr.fs_specific,
				     &edrfsattr);

  printf("ATTR: Type 0x%02X\tMode %06o\tNlink %u\tUid %u\tGid %u\tClean %u\t"
	 "Len %" NASD_64u_FMT "\n", edrfsattr.type, edrfsattr.mode,
	 edrfsattr.nlink, edrfsattr.uid, edrfsattr.gid, edrfsattr.clean,
	 attr.object_len);
  printf("ATTR: Block- Prealloc\t%" NASD_64u_FMT "\tUsed %" NASD_64u_FMT
	 "\tSize %u\n", attr.block_preallocation, attr.blocks_used,
	 attr.block_size);
  printf("ATTR: Time - AttrMod\t%d:%09d\tObjMod\t%d:%09d\n",
	 attr.attr_modify_time.ts_sec, attr.attr_modify_time.ts_nsec,
	 attr.object_modify_time.ts_sec, attr.object_modify_time.ts_nsec);
  printf("ATTR: Time - ObjCrt\t%d:%09d\n",
	 attr.object_create_time.ts_sec, attr.object_create_time.ts_nsec);
  printf("ATTR: Time - FsAttrMod\t%d:%09d\tFsObjMod %d:%09d\n",
	 attr.fs_attr_modify_time.ts_sec, attr.fs_attr_modify_time.ts_nsec,
	 attr.fs_object_modify_time.ts_sec,attr.fs_object_modify_time.ts_nsec);
}
