// -*- mode: Java; file-coding-system: *euc-japan*; -*-
// $Id: Marshal.java,v 1.4 1999/10/05 14:25:29 yamauchi Exp $
// Copyright (C) 1999 MAEDA Atusi

package jp.ac.tohoku.is.archi.mpi2.util;

import mpi.Datatype;
import mpi.MPI;

/**
 * {@link EncodedData}を生成するためのクラス.
 */
public abstract class Marshal {
  interface EncodedDataFactory {
    EncodedData create();
  }
  static class ByteArrayEncodedDataFactory implements EncodedDataFactory {
    public EncodedData create() {
      return new ByteArrayEncodedData();
    }
  }
  static class DoubleArrayEncodedDataFactory implements EncodedDataFactory {
    public EncodedData create() {
      return new DoubleArrayEncodedData();
    }
  }
  private static EncodedDataFactory factory =
    new DoubleArrayEncodedDataFactory();

  /**
   * {@link EncodedData}を1つ生成して返す.
   */
  public static EncodedData createEncodedData() {
    return factory.create();
  }

  /**
   * データを1つだけ送信するための{@link EncodedData}を作る convenience method.
   * EncodedDataを作り、{@link Encodable}をその中にencodeして、すぐ送信
   * できる状態にして返す．
   * @param data 送信すべきデータ
   */
  public static EncodedData createEncodedData(Encodable data) {
    EncodedData e = factory.create();
    Encoder encoder = e.getEncoder();
    encoder.prepare(data);
    encoder.finishPrepare();
    encoder.encode(data);
    encoder.finishEncode();
    return e;
  }

  /**
   * 同じ型の{@link Encodable}の配列を送信するための{@link EncodedData}
   * を作る convenience method.
   * EncodedDataを作り、{@link Encodable}配列の内容をその中にencodeして、
   * すぐ送信できる状態にして返す．
   * @param array 送信すべきEncodableの配列．中のEncodableはすべて同じ
   * サイズのオブジェクトでなければならない.
   */
  public static EncodedData createEncodedData(Encodable[] array) {
      EncodedData e = factory.create();
      
      // System.out.println("DEBUG : e " + e);

      Encoder encoder = e.getEncoder();

      // System.out.println("DEBUG : encoder " + encoder);

      encoder.allocate(array[0], array.length);

      // System.out.println("DEBUG : allcate onesize " + array[0].sizeInDouble() 
      // + ", len " + array.length); DELETEME

      for (int i = 0; i < array.length; i++) {
	  // System.out.println("DEBUG : [" + i + "]");	  
	  encoder.encode(array[i]);
      }
      // System.out.println("DEBUG encode");	  
      encoder.finishEncode();
      // System.out.println("DEBUG finish");	  
      return e;
  }

  /**
   * データを1つだけ受信するための{@link EncodedData}を作る convenience method.
   * EncodedDataを作り、データを1つ受信する大きさのバッファを割り付け，すぐ受信
   * できる状態にして返す．
   * @param od Encodableの大きさを知らせるためのObjectDecoder
   */
  public static EncodedData createEncodedData(ObjectDecoder od) {
    EncodedData e = factory.create();
    Decoder decoder = e.getDecoder();
    decoder.prepare(od);
    decoder.finishPrepare();
    return e;
  }

  /**
   * 同じ型の{@link Encodable}の配列を受信するための{@link EncodedData}
   * を作る convenience method.
   * EncodedDataを作り、配列を受信できるだけのデータを割り付け，すぐ受信
   * できる状態にして返す．
   * @param od 受信する予定のEncodableの大きさを示すObjectDecoder
   * @param nelem 受信するEncodable配列の要素数
   */
  public static EncodedData createEncodedData(ObjectDecoder od, int nelem) {
    EncodedData e = factory.create();
    Decoder decoder = e.getDecoder();
    decoder.allocate(od, nelem);
    return e;
  }

  /**
   * 決まった大きさのデータを受信するための{@link EncodedData}を作る 
   * convenience method.
   * EncodedDataを作り、指定した大きさのバッファを割り付け，すぐ受信
   * できる状態にして返す．
   * @param size Encodableの大きさ(配列の要素数)
   */
  public static EncodedData createEncodedData(int size) {
    EncodedData e = factory.create();
    Decoder decoder = e.getDecoder();
    decoder.allocate(size);
    return e;
  }

  private static Datatype mode = MPI.DOUBLE;
  /**
   * {@link EncodedData}の実装に，byte[]を用いたものとdouble[]を用いたもの
   * のどちらを使うかを切り替える.
   * @param mode MPI.BYTEまたはMPI.DOUBLE
   */
  public static void setMode(Datatype mode) {
    if (mode == MPI.BYTE) {
      factory = new ByteArrayEncodedDataFactory();
      Marshal.mode = mode;
    } else if (mode == MPI.DOUBLE) {
      factory = new DoubleArrayEncodedDataFactory();
      Marshal.mode = mode;
    } else {
      throw new RuntimeException("Illegal mode: " + mode);
    }
  }

  /**
   * 現在，{@link #createEncodedData}を呼び出すと，byte[]を用いた実装と
   * double[]を用いた実装のどちらが返るかのモードを返す.
   * @returns MPI.BYTEまたはMPI.DOUBLE
   */
  public static Datatype getMode() {
    return mode;
  }
}
