package org.mykarte.service.retrieve;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.mykarte.service.retrieve.impl.AttachmentFactory;
import org.mykarte.service.retrieve.impl.IAttachment;
import org.mykarte.util.DateUtils;
import org.mykarte.ws.attachment.proxy.AttachmentServiceHttpSoap11EndpointClient;


/**
 * RetrieveService クラス
 */
public class RetrieveService {
  // Logging (log4j)
  private Log log = LogFactory.getLog(this.getClass());

  private IAttachment attachment = null;

  private String endpoint = "";
  private String facilityId = "";
  private String patientId = "";
  private String encryptedId = "";
  private String type = "";
  private String attribute = "";
  private String serviceId = "";

  private boolean auth = false;

  /**
   * 認証する。
   * 
   * @param args 引数
   * @return true：OK, false：NG
   */
  public boolean authenticate(String[] args) {
    IAttachment attachment = getAttachment();

    auth = attachment.authenticate(args);

    return auth;
  }

  /**
   * コールバック情報を設定する。
   * 
   * @param info コールバック情報
   */
  public void setCallbackInfo(String[] info) throws Exception {
    checkAuth();

    // エンドポイント
    endpoint = info[0];

    // 施設ID
    facilityId = info[1];

    // 暗号化ID
    encryptedId = info[2];

    // サービスID
    serviceId = info[3];
  }

  /**
   * 患者IDを取得する。
   * 
   * @return 患者ID
   */
  public String getPatient() throws Exception {
    checkAuth();

    IAttachment attachment = getAttachment();

    return attachment.getPatient();
  }

  /**
   * 患者IDを設定する。
   * 
   * @param patientId 患者ID
   */
  public void setPatient(String patientId) throws Exception {
    checkAuth();

    IAttachment attachment = getAttachment();

    this.patientId = patientId;

    attachment.setPatient(patientId);
  }

  /**
   * type, attribute を設定する。
   * 
   * @param props type, attribute
   */
  public void setProperties(String[] props) throws Exception {
    checkAuth();

    IAttachment attachment = getAttachment();

    // タイプ
    type = props[0];

    // 属性
    attribute = props[1];

    attachment.setProperties(props);
  }

  /**
   * 日付を取得する。
   * 
   * @param startDate 開始日
   * @param endDate 終了日
   * @param diff 差分
   * @return 日付
   */
  public String[] getDates(String startDate, String endDate, 
                           boolean diff) throws Exception {
    checkAuth();

    IAttachment attachment = getAttachment();

    Calendar sd = Calendar.getInstance();
    sd.setTime(DateUtils.parse(startDate));

    Calendar ed = Calendar.getInstance();
    ed.setTime(DateUtils.parse(endDate));

    Calendar[] dates = attachment.getDates(sd, ed, diff);

    String[] results = new String[dates.length];

    for (int i = 0; i < dates.length; i++) {
      results[i] = 
          DateUtils.format(dates[i].getTime(), DateUtils.ISO_DATE_FORMAT);
    }

    return results;
  }

  /**
   * 指定した日付のデータを文字列として取得する。
   * 
   * @param date 日付
   * @return 文字列
   */
  public String getContent(String date) throws Exception {
    checkAuth();

    IAttachment attachment = getAttachment();

    Calendar d = Calendar.getInstance();
    d.setTime(DateUtils.parse(date));

    return attachment.getContent(d);
  }

  /**
   * 指定した日付のデータをファイルとして取得する。
   * 
   * @param date 日付
   * @return 結果
   */
  public String getFiles(String date) throws Exception {
    checkAuth();

    IAttachment attachment = getAttachment();

    //------------------------//
    //   Attachmentサービス   //
    //------------------------//    
    AttachmentServiceHttpSoap11EndpointClient attachmentService = 
      new AttachmentServiceHttpSoap11EndpointClient();

    attachmentService.setMaintainSession(true);

    // エンドポイント
    attachmentService.setEndpoint(endpoint);

    if (!attachmentService.authenticate(new String[] { facilityId, 
                                                       encryptedId })) {
      throw new Exception("AttachmentServiceの認証に失敗しました。");
    }

    // サービスID
    attachmentService.setServiceId(serviceId);

    // 患者ID
    attachmentService.setPatientId(patientId);

    // タイプ
    attachmentService.setType(type);

    // 属性
    attachmentService.setAttribute(attribute);

    // 日付
    Calendar d = Calendar.getInstance();
    d.setTime(DateUtils.parse(date));
    attachmentService.setDate(d);

    Map<String, String> files = attachment.getFiles(d);
    boolean[] results = new boolean[files.size()];

    int i = 0;
    for (Map.Entry<String, String> entry: files.entrySet()) {
      // 拡張子
      String extension = getExtension(entry.getKey());

      if (StringUtils.isBlank(extension)) {
        throw new Exception("ファイルの拡張子がありません。");
      }

      attachmentService.setExtension(extension);

      // DescriptionID
      String descriptionId = entry.getValue();

      if (StringUtils.isEmpty(descriptionId)) {
        attachmentService.setDescriptionId("");
      } else {
        attachmentService.setDescriptionId(descriptionId);
      }

      try {
        // ファイルデータ
        BufferedInputStream bin = 
          new BufferedInputStream(new FileInputStream(entry.getKey()));
        byte[] fileData = new byte[bin.available()];
        bin.read(fileData);
        bin.close();

        results[i] = attachmentService.store(i, fileData);
      } catch (Exception ex) {
        ex.printStackTrace();
      }

      i++;
    }

    boolean result;
    boolean[] temp = new boolean[results.length];

    Arrays.fill(temp, true);
    result = Arrays.equals(results, temp);

    if (result) {
      // 全件成功
      return "Success";
    }

    Arrays.fill(temp, false);
    result = Arrays.equals(results, temp);

    if (result) {
      // 全件失敗
      return "Failure";
    }

    // 一部失敗
    return "Warning";
  }

  /**
   * IAttachment を取得する。
   * 
   * @return IAttachment
   */
  private IAttachment getAttachment() {
    if (attachment == null) {
      attachment = AttachmentFactory.create();
    }

    return attachment;
  }

  /**
   * 認証されているか確認する。
   */
  private void checkAuth() throws Exception {
    if (!auth) {
      throw new Exception("認証されていません。");
    }
  }

  /**
   * ファイルの拡張子を取得する。
   * 
   * @param path ファイルパス
   * @return 拡張子
   */
  private String getExtension(String path) {
    String fileName = new File(path).getName();

    int index = fileName.lastIndexOf(".");
    String extension = fileName.substring(index + 1).toLowerCase();

    return extension;
  }
}
