////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
//
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC
// file. If your distribution of this software does not contain
// a LICENSE.DOC file then you have no rights to use this
// software in any manner and should contact DSTC at the address
// below to determine an appropriate licensing arrangement.
//
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
//
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
//
////////////////////////////////////////////////////////////////////////////

package com.dstc.security.asn1;

import java.io.IOException;

/**
 * <p>A class representing the ASN.1 Primitive type.
 *
 * @see com.dstc.security.asn1.Asn1
 *
 * @version 0.98, 98/07/01
 * @author Ming Yung
 */
public class Primitive extends Asn1
{
  protected byte contents[];

  public static Asn1 decode(byte encoded[],
      int marks[]) throws Asn1Exception
  {
    //Test tag and delegate accordingly
    byte cl = (byte)(encoded[marks[0]] & MASK_CLASS);
    byte tag = (byte)(encoded[marks[0]] & MASK_NUMBER);

    if (cl == TAG_CLASS_UNIVERSAL)
    {
      switch (tag)
      {
        case TAG_BOOLEAN:
          return Boolean.decode(encoded, marks);

        case TAG_INTEGER:
          return Integer.decode(encoded, marks);

        case TAG_BITSTRING:
          return BitString.decode(encoded, marks);

        case TAG_NULL:
          return Null.decode(encoded, marks);

        case TAG_OID:
          return Oid.decode(encoded, marks);

        case TAG_UTF8STRING:
          return UTF8String.decode(encoded, marks);

        case TAG_OCTETSTRING:
          return OctetString.decode(encoded, marks);

        case TAG_PRINTABLESTRING:
          return PrintableString.decode(encoded, marks);

        case TAG_T61STRING:
          return T61String.decode(encoded, marks);

        case TAG_GENERALSTRING:
          return GeneralString.decode(encoded, marks);

        case TAG_IA5STRING:
          return IA5String.decode(encoded, marks);

        case TAG_UTCTIME:
          return UTCTime.decode(encoded, marks);

        case TAG_ENUMERATED:
          return Enumerated.decode(encoded, marks);

        case TAG_GENERALIZEDTIME:
          return GeneralizedTime.decode(encoded, marks);

        case TAG_BMPSTRING:
          return BMPString.decode(encoded, marks);

        default:
          throw new Asn1Exception("Unknown universal tag " + tag);
      }
    }
    else if (cl == TAG_CLASS_CONTEXT)
    {
      Primitive implicit = new Primitive();
      implicit.doDecode(encoded, marks);
      implicit.classType = TAG_CLASS_CONTEXT;
      implicit.tagNum = tag;
      return implicit;
    }
    else
    {
      throw new Asn1Exception("unknown tag " + tag);
    }
  }

  public Primitive() {}
 
  protected Primitive(byte tag, Asn1 orig)
  {
    this.encoded = (byte[])orig.encode().clone();
    this.encoded[0] = tag;
  }

  protected byte[] doContents() throws IOException
  {
    return contents;
  }

  protected void doTag(byte encoding[])
  {
    encoding[0] = (byte)(tagNum | classType);
  }

  protected void doDecode(byte encoded[], int marks[])
  {
    //Sets up pointers to start and end of contents
    int info = decodeLengthOctets(encoded, marks);
    if (info < 0)
      return;
    contents = new byte[marks[1] - info];
    System.arraycopy (encoded, info, contents, 0, contents.length);
  }

  protected void printHex(byte bytes[])
  {
    StringBuffer sb = new StringBuffer();

    if (bytes != null)
    {
      for (int i = 0; i < bytes.length; i++)
      {
        if (i % 16 == 0)
        {
          sb.append("\n ");
          for (int j = 0; j < this.depth; j++)
          {
            sb.append("  ");
          }
        }
        sb.append(hexDigit(bytes[i]) + " ");
      }
    }
    System.out.println(sb.toString());
  }

  public byte[] getBytes()
  {
    return contents;
  }

  private static final String hexDigit(byte x)
  {
    char ret[] = new char[2];
    char c = (char)((x >> 4) & 0xf);
    if (c > 9)
    {
      c = (char)((c - 10) + 'a');
    }
    else
    {
      c = (char)(c + '0');
    }
    ret[0] = c;

    c = (char)(x & 0xf);
    if (c > 9)
    {
      c = (char)((c - 10) + 'a');
    }
    else
    {
      c = (char)(c + '0');
    }
    ret[1] = c;
    return new String(ret);
  }

  public void info(int dep)
  {
    depth = dep + 1;
    spit();
    System.out.print("IMPLICIT ");
    printHex(contents);
  }
}
