package btdex.ui;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import bt.BT;
import bt.compiler.Compiler;
import btdex.core.*;

import static btdex.locale.Translation.tr;
import btdex.sc.SellContract;
import burst.kit.crypto.BurstCrypto;
import burst.kit.entity.BurstValue;
import burst.kit.entity.response.TransactionBroadcast;
import io.reactivex.Single;

public class RegisterContractDialog extends JDialog implements ActionListener, ChangeListener {
	private static final long serialVersionUID = 1L;

	JTextPane conditions;
	JCheckBox acceptBox;

	JSpinner numOfContractsSpinner;

	JPasswordField pin;

	private JButton okButton;
	private JButton cancelButton;

	private Compiler contract;

	private boolean isBuy;

	public RegisterContractDialog(Window owner, boolean isBuy) {
		super(owner, ModalityType.APPLICATION_MODAL);
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		
		this.isBuy = isBuy;

		setTitle(tr("reg_register"));

		conditions = new JTextPane();
		conditions.setPreferredSize(new Dimension(240, 220));
		acceptBox = new JCheckBox(tr("dlg_accept_terms"));

		// The number of contracts to register
		SpinnerNumberModel numModel = new SpinnerNumberModel(1, 1, 10, 1);
		numOfContractsSpinner = new JSpinner(numModel);
		JPanel numOfContractsPanel = new Desc(tr("reg_num_contracts"), numOfContractsSpinner);
		numOfContractsSpinner.addChangeListener(this);

		// Create a button
		JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));

		pin = new JPasswordField(12);
		pin.addActionListener(this);

		cancelButton = new JButton(tr("dlg_cancel"));
		okButton = new JButton(tr("dlg_ok"));

		cancelButton.addActionListener(this);
		okButton.addActionListener(this);

		buttonPane.add(new Desc(tr("dlg_pin"), pin));
		buttonPane.add(new Desc(" ", cancelButton));
		buttonPane.add(new Desc(" ", okButton));

		JPanel content = (JPanel)getContentPane();
		content.setBorder(new EmptyBorder(4, 4, 4, 4));

		JPanel conditionsPanel = new JPanel(new BorderLayout());
		conditionsPanel.setBorder(BorderFactory.createTitledBorder(tr("dlg_terms_and_conditions")));
		JScrollPane scroll = new JScrollPane(conditions);
		scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		scroll.setPreferredSize(conditions.getPreferredSize());
		conditionsPanel.add(scroll, BorderLayout.CENTER);

		conditionsPanel.add(acceptBox, BorderLayout.PAGE_END);

		JPanel centerPanel = new JPanel(new BorderLayout());
		centerPanel.add(conditionsPanel, BorderLayout.CENTER);

		content.add(numOfContractsPanel, BorderLayout.PAGE_START);
		content.add(centerPanel, BorderLayout.CENTER);
		content.add(buttonPane, BorderLayout.PAGE_END);

		contract = Contracts.getCompiler(isBuy ? ContractType.BUY : ContractType.SELL);
		stateChanged(null);
		pack();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == cancelButton) {
			setVisible(false);
		}

		if(e.getSource() == okButton || e.getSource() == pin) {
			String error = null;
			Globals g = Globals.getInstance();

			if(error == null && !acceptBox.isSelected()) {
				error = tr("dlg_accept_first");
				acceptBox.requestFocus();
			}

			if(error == null && !g.checkPIN(pin.getPassword())) {
				error = tr("dlg_invalid_pin");
				pin.requestFocus();
			}

			if(error!=null) {
				Toast.makeText((JFrame) this.getOwner(), error, Toast.Style.ERROR).display(okButton);
				return;
			}

			// all set, lets register the contract
			try {
				setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

				int ncontracts = Integer.parseInt(numOfContractsSpinner.getValue().toString());

				for (int c = 0; c < ncontracts; c++) {
					long data[] = Contracts.getNewContractData(g.isTestnet());

					ByteBuffer dataBuffer = ByteBuffer.allocate(data==null ? 0 : data.length*8);
					dataBuffer.order(ByteOrder.LITTLE_ENDIAN);
					for (int i = 0; data!=null && i < data.length; i++) {
						dataBuffer.putLong(data[i]);
					}

					byte[] creationBytes = BurstCrypto.getInstance().getATCreationBytes((short)2,
							contract.getCode(), dataBuffer.array(), (short)contract.getDataPages(), (short)1, (short)1,
							BurstValue.fromPlanck(SellContract.ACTIVATION_FEE));

					Single<TransactionBroadcast> tx = g.getNS().generateCreateATTransaction(g.getPubKey(),
							BT.getMinRegisteringFee(contract),
							Constants.BURST_DEADLINE, "BTDEX", "BTDEX sell contract " + System.currentTimeMillis(), creationBytes)
							.flatMap(unsignedTransactionBytes -> {
								byte[] signedTransactionBytes = g.signTransaction(pin.getPassword(), unsignedTransactionBytes);
								return g.getNS().broadcastTransaction(signedTransactionBytes);
							});

					TransactionBroadcast tb = tx.blockingGet();
					tb.getTransactionId();
					setVisible(false);

					Toast.makeText((JFrame) this.getOwner(),
							tr("send_tx_broadcast", tb.getTransactionId().toString()), Toast.Style.SUCCESS).display();	
				}
			}
			catch (Exception ex) {
				Toast.makeText((JFrame) this.getOwner(), ex.getMessage(), Toast.Style.ERROR).display(okButton);
			}
			setCursor(Cursor.getDefaultCursor());
		}
	}

	@Override
	public void stateChanged(ChangeEvent evt) {
		Integer ncontracts = Integer.parseInt(numOfContractsSpinner.getValue().toString());
		StringBuilder terms = new StringBuilder();
		terms.append(tr("reg_terms", ncontracts,
				tr(isBuy ? "reg_buying" : "reg_selling"),
				NumberFormatting.BURST.format(BT.getMinRegisteringFee(contract).longValue())));
		terms.append("\n\n").append(tr("reg_terms_closing",
				tr(isBuy ? "reg_buying" : "reg_selling") ));
		conditions.setText(terms.toString());
		conditions.setCaretPosition(0);
	}
}