package com.dgex.offspring.ui;

import java.util.ArrayList;
import java.util.List;

import nxt.Account;
import nxt.Asset;
import nxt.Constants;
import nxt.NxtException.ValidationException;
import nxt.Transaction;
import nxt.util.Convert;

import org.apache.log4j.Logger;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;

import com.dgex.offspring.nxtCore.service.IAccount;
import com.dgex.offspring.nxtCore.service.INxtService;
import com.dgex.offspring.nxtCore.service.TransactionException;
import com.dgex.offspring.swt.wizard.GenericTransactionWizard;
import com.dgex.offspring.swt.wizard.IGenericTransaction;
import com.dgex.offspring.swt.wizard.IGenericTransactionField;
import com.dgex.offspring.user.service.IUser;
import com.dgex.offspring.user.service.IUserService;

public class TransferAssetWizard extends GenericTransactionWizard {

  // In order to send an asset we need the following fields;
  //
  // String recipientValue = req.getParameter("recipient");
  // String assetValue = req.getParameter("asset");
  // String quantityValue = req.getParameter("quantity");
  //
  // Since a user can only send assets he actually owns the assetValue string is
  // selected from a dropdown that lists all assets owned by an account

  static Logger logger = Logger.getLogger(TransferAssetWizard.class);

  final IGenericTransactionField fieldRecipient = new IGenericTransactionField() {

    private Text textRecipient;
    private Text textRecipientReadonly;

    @Override
    public String getLabel() {
      return "Recipient";
    }

    @Override
    public Object getValue() {
      String recipientValue = textRecipient.getText().trim();
      try {
        return Convert.parseUnsignedLong(recipientValue);
      }
      catch (RuntimeException e) {
        logger.error("Parse Recipient ID", e);
      }
      return null;
    }

    @Override
    public Control createControl(Composite parent) {
      textRecipient = new Text(parent, SWT.BORDER);
      textRecipient.setText("");
      textRecipient.addModifyListener(new ModifyListener() {

        @Override
        public void modifyText(ModifyEvent e) {
          requestVerification();
        }
      });
      return textRecipient;
    }

    @Override
    public Control createReadonlyControl(Composite parent) {
      textRecipientReadonly = new Text(parent, SWT.BORDER);
      textRecipientReadonly.setText("");
      textRecipientReadonly.setEditable(false);
      return textRecipientReadonly;
    }

    @Override
    public boolean verify(String[] message) {
      String recipientValue = textRecipient.getText().trim();
      if ("0".equals(recipientValue)) {
        message[0] = "Missing recipient";
        return false;
      }

      try {
        Convert.parseUnsignedLong(recipientValue);
      }
      catch (RuntimeException e) {
        message[0] = "Incorrect recipient";
        return false;
      }
      textRecipientReadonly.setText(recipientValue);
      return true;
    }
  };

  final IGenericTransactionField fieldAsset = new IGenericTransactionField() {

    private final List<Asset> assets = new ArrayList<Asset>();
    private Combo comboAsset;
    private Combo comboAssetReadonly;
    private IUser currentUser;
    private final SelectionListener selectionListener = new SelectionAdapter() {

      @Override
      public void widgetSelected(SelectionEvent e) {
        requestVerification();
      }
    };

    @Override
    public String getLabel() {
      return "Asset";
    }

    @Override
    public Object getValue() {
      return assets.get(comboAsset.getSelectionIndex()); // Asset
    }

    private void populateAssets(Account account) {
      comboAsset.removeAll();
      assets.clear();
      for (Long assetId : account.getAssetBalances().keySet()) {
        Asset asset = Asset.getAsset(assetId);
        comboAsset.add(createLabel(account, asset));
        assets.add(asset);
      }
    }

    private String createLabel(Account account, Asset asset) {
      int balance = account.getAssetBalances().get(asset.getId());
      return "Asset: " + asset.getName() + " Balance: " + balance;
    }

    @Override
    public Control createControl(Composite parent) {
      comboAsset = new Combo(parent, SWT.READ_ONLY);

      currentUser = (IUser) fieldSender.getValue();
      if (!currentUser.getAccount().isReadOnly()) {
        populateAssets(currentUser.getAccount().getNative());
      }
      comboAsset.select(0);
      comboAsset.addSelectionListener(selectionListener);
      return comboAsset;
    }

    @Override
    public Control createReadonlyControl(Composite parent) {
      comboAssetReadonly = new Combo(parent, SWT.READ_ONLY);
      // comboAssetReadonly.add("..");
      // comboAssetReadonly.select(0);
      return comboAssetReadonly;
    }

    @Override
    public boolean verify(String[] message) {

      /* Update readonly combo */
      Account account = user.getAccount().getNative();
      Asset asset = (Asset) getValue();
      comboAssetReadonly.removeAll();
      if (asset != null) {
        comboAssetReadonly.add(createLabel(account, asset));
        comboAssetReadonly.select(0);
      }

      /* There might have been a user change must update the list of assets */
      IUser sender = (IUser) fieldSender.getValue();
      if (!sender.equals(currentUser)) {
        currentUser = sender;
        comboAsset.removeAll();
        if (!sender.getAccount().isReadOnly()) {
          populateAssets(sender.getAccount().getNative());
        }
        comboAsset.removeSelectionListener(selectionListener);
        comboAsset.select(0);
        comboAsset.addSelectionListener(selectionListener);
      }
      return true;
    }
  };

  final IGenericTransactionField fieldAmount = new IGenericTransactionField() {

    private Text textAmount;
    private Text textAmountReadonly;

    @Override
    public String getLabel() {
      return "Amount";
    }

    @Override
    public Object getValue() {
      return Integer.parseInt(textAmount.getText().trim());
    }

    @Override
    public Control createControl(Composite parent) {
      textAmount = new Text(parent, SWT.BORDER);
      textAmount.setText("0");
      textAmount.addModifyListener(new ModifyListener() {

        @Override
        public void modifyText(ModifyEvent e) {
          requestVerification();
        }
      });
      return textAmount;
    }

    @Override
    public Control createReadonlyControl(Composite parent) {
      textAmountReadonly = new Text(parent, SWT.BORDER);
      textAmountReadonly.setEditable(false);
      return textAmountReadonly;
    }

    @Override
    public boolean verify(String[] message) {
      String amountValue = textAmount.getText().trim();
      try {
        int amount = Integer.parseInt(amountValue);
        if (amount <= 0 || amount >= Constants.MAX_ASSET_QUANTITY) {
          message[0] = "Incorrect amount";
          return false;
        }
      }
      catch (NumberFormatException e) {
        message[0] = "Amount must be numeric";
        return false;
      }
      textAmountReadonly.setText(amountValue);
      return true;
    }
  };

  public TransferAssetWizard(final IUserService userService,
      final INxtService nxt) {
    super(userService);
    setWindowTitle("Transfer Asset");
    setTransaction(new IGenericTransaction() {

      @Override
      public String sendTransaction(String[] message) {

        IAccount sender = user.getAccount();
        Long recipient = (Long) fieldRecipient.getValue();
        int amount = (Integer) fieldAmount.getValue();
        Asset asset = (Asset) fieldAsset.getValue();

        PromptFeeDeadline dialog = new PromptFeeDeadline(getShell());
        if (dialog.open() != Window.OK) {
          message[0] = "Invalid fee and deadline";
          return null;
        }
        int fee = dialog.getFee();
        short deadline = dialog.getDeadline();

        try {
          Transaction t = nxt.createTransferAssetTransaction(sender, recipient,
              asset.getId(), amount, deadline, fee, null);
          return t.getStringId();
        }
        catch (ValidationException e) {
          message[0] = e.getMessage();
        }
        catch (TransactionException e) {
          message[0] = e.getMessage();
        }
        return null;
      }

      @Override
      public IGenericTransactionField[] getFields() {
        return new IGenericTransactionField[] { fieldSender, fieldRecipient,
            fieldAsset, fieldAmount };
      }

      @Override
      public boolean verifySender(String message[]) {
        if (user == null) {
          message[0] = "Invalid sender";
          return false;
        }
        if (user.getAccount().isReadOnly()) {
          message[0] = "This is a readonly account";
          return false;
        }

        Account account = user.getAccount().getNative();
        Asset asset = (Asset) fieldAsset.getValue();
        int amount = (Integer) fieldAmount.getValue();
        if (asset != null) {
          Integer assetBalance = account.getUnconfirmedAssetBalance(asset
              .getId());
          if (assetBalance == null || amount > assetBalance) {
            message[0] = "Insufficient Asset Balance";
            return false;
          }
        }

        if (user.getAccount().getBalance() < 1) {
          message[0] = "Insufficient Balance";
          return false;
        }
        return true;
      }
    });
  }
}