package com.enactor.pos.swing;

import com.enactor.core.application.process.IApplicationProcessDataType;
import com.enactor.core.automation.AUT.AutomationException;
import com.enactor.core.automation.component.IComponentAccessor;
import com.enactor.core.automation.component.IWindowAccessor;
import com.enactor.core.utilities.Logger;
import com.enactor.core.utilities.StringUtils;
import com.enactor.pos.PosTestConstants;
import com.enactor.pos.test.BaseStepsProvider;

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

/**
 * Base class for the POS test steps.
 *
 * @author Thushara.Ramasinghe
 */
public class SwingPosStepsProvider extends BaseStepsProvider {

    private static final Logger logger = Logger.getLogger(SwingPosStepsProvider.class);
    private static final String RECEIPT_COMPONENT_NAME = "Receipt";
    private static final int WAIT_CHECK_COMPONENT_NOT_DISPLAY = 1000;

    private final ISwingAppStepsProvider swingAppStepsProvider =
            new SwingAppStepsProvider(getController(), WAIT_WINDOW_DISPLAY);

    @Override
    public void checkWindow(String windowName) throws AutomationException {
        swingAppStepsProvider.waitForWindow(getWindowId(windowName), getConfig().getInt(PosTestConstants.WAIT_STARTUP));
    }

    @Override
    public void waitsForComponent(String component, String windowName) throws AutomationException {
        swingAppStepsProvider.waitsForComponent(getComponentId(component), getWindowId(windowName), WAIT_COMPONENT_DISPLAY);
    }
    
	@Override
	public IComponentAccessor getComponentAccessor(String componentId, String windowId)
			throws AutomationException {
		return swingAppStepsProvider.waitsForComponent(
				getComponentId(componentId),
				getWindowId(windowId), WAIT_WINDOW_DISPLAY);
	}
    
    @Override
    public void checkComponentNotDisplay(String component, String windowName) throws AutomationException {
        try {
            swingAppStepsProvider.waitsForComponent(
                    getComponentId(component), getWindowId(windowName), WAIT_CHECK_COMPONENT_NOT_DISPLAY);
        } catch (AutomationException e) {
            return;
        }
        throw new AutomationException("Component [" + component + "] display on [" + windowName + "] page");
    }

    @Override
    public String getComponentInnerContent(String component, String windowName) throws AutomationException {
        return swingAppStepsProvider.getComponentText(getComponentId(component), getWindowId(windowName));
    }

    @Override
    public void pressButton(String buttonLabel, String windowName) throws AutomationException {
        pressButtonForGivenWindowId(buttonLabel, WAIT_BUTTON_DISPLAY, getWindowId(windowName), TRANSACTION_BASKET_WINDOW_NAME.equals(windowName));
    }

    @Override
    public void pressButtonByComponent(String componentName, String windowName) throws AutomationException {
        swingAppStepsProvider.pressButtonByComponentId(getComponentId(componentName), getWindowId(windowName));
    }

    @Override
    public void checkButtonNotDisplay(String buttonLabel, String windowName) throws AutomationException {
        swingAppStepsProvider.assertButtonWithLabelNotDisplayed(buttonLabel, getWindowId(windowName),
                getPosButtonComponentIdList(), WAIT_BUTTON_DISPLAY);
    }

    @Override
    public void enterText(String text, String field, String windowName) throws AutomationException {
        text = checkCalculationText(text);
        swingAppStepsProvider.enterText(getTestData(text), getComponentId(field), getWindowId(windowName));
    }

    @Override
    public void enterRichText(String text, String field, String windowName) throws AutomationException{
        enterText(text,field,windowName);
    }

    @Override
    public void checkTextFieldValue(String text, String field, String windowName) throws AutomationException {
        swingAppStepsProvider.assertTextFieldHasEqualContent(getTestData(text), getComponentId(field), getWindowId(windowName));
    }

    @Override
    public void checkTextFieldContainsValue(String text, String field, String windowName) throws AutomationException {
        swingAppStepsProvider.assertTextFieldContainsValue(getTestData(text), getComponentId(field), getWindowId(windowName));
    }

    @Override
    public void checkFieldIsNotEmpty(String componentName, String windowName) throws AutomationException {
        swingAppStepsProvider.assertTextFieldIsNotEmpty(getComponentId(componentName), getWindowId(windowName));
    }

    @Override
    public void checkLabelFieldValue(String text, String field, String windowName) throws AutomationException {
        swingAppStepsProvider.assertLabelHasEqualContent(getTestData(text), getComponentId(field), getWindowId(windowName));
    }

    @Override
    public void selectListItem(String item, String listName, String windowName) throws AutomationException {
        swingAppStepsProvider.selectListItem(getTestData(item), getComponentId(listName), getWindowId(windowName));
    }

    @Override
    public void selectComboboxItem(String item, String field, String windowName) throws AutomationException {
        swingAppStepsProvider.selectComboboxItem(getTestData(item), getComponentId(field), getWindowId(windowName));
    }

    @Override
    public void checkPanelTextDisplayed(String text, String panel, String windowName) throws AutomationException {
        swingAppStepsProvider.assertPanelHasEqualContent(getTestData(text), getComponentId(panel), getWindowId(windowName));
    }

    @Override
    public void afterTestCheckEnterUserScreenShown(String testName, boolean clearTestPrinter, boolean testFailed) throws Exception {
        try {
            TRANSACTION_IDS.clear();
            if (testFailed) {
                failTest(testName);
            }
            swingAppStepsProvider.waitForWindow(getWindowId(ENTER_USER_WINDOW_NAME), WAIT_WINDOW_DISPLAY);
        } catch (Exception e) {
            recoverApplication(); //navigate to transaction basket page
            restartApplication();
        } finally {
            if (clearTestPrinter) {
                clearPrinter();
            }
        }
    }

    @Override
    public byte[] requestCaptureScreenshot() throws AutomationException {
        return getController().requestCaptureScreenshot();
    }

    /**
     * Ask the automation server to restart the application. Note, this will
     * not create a new JVM; in practice we kill the view and then re-run
     * the main application.
     *
     * @throws AutomationException
     */
    @Override
    public void restartApplication() throws AutomationException {
        restartSwingApp(swingAppStepsProvider);
    }
    
    @Override
    public void addToViewData(IApplicationProcessDataType type, Object data) throws AutomationException {
    	if (data instanceof String) {
        	getController().setViewData(type, getTestData((String) data));
    	} else {
        	getController().setViewData(type, data);

    	}
    }

    @Override
    protected void pressButtonByComponentInCurrentWindow(String buttonLabel, long timeout) throws AutomationException {
        swingAppStepsProvider.pressButtonByComponentId(getComponentId(buttonLabel), getActiveWindowId(), timeout);
    }

    @Override
    protected void pressButtonInCurrentWindow(String buttonLabel, long timeout) throws AutomationException {
        swingAppStepsProvider.pressButtonWithLabel(buttonLabel, getActiveWindowId(), getPosButtonComponentIdList(), timeout);
    }

    @Override
    protected void checkWindow(String windowName, long timeout) throws AutomationException {
        swingAppStepsProvider.waitForWindow(getWindowId(windowName), timeout);
    }

    @Override
    protected List<String> getTextInListItems(String list, String windowName) throws AutomationException {
        List<String> textLines = new ArrayList<>();
        IWindowAccessor window = getSwingWindow(windowName);
        List<List<String>> lines = window.findComponent(getComponentId(list)).getDialogPanel();
        for (List<String> line : lines) {
            if (line != null && !line.isEmpty()) {
                textLines.add(StringUtils.joinNotEmpty(" ", line.toArray(new String[line.size()])));
            }
        }
        return textLines;
    }

    /**
     * Press the button with the given button label for the given given window id
     *
     * @param buttonLabel
     * @param timeout
     * @param windowId
     * @throws AutomationException
     */
    private void pressButtonForGivenWindowId(String buttonLabel, long timeout, String windowId, boolean waitForPanelSizeUpdate) throws AutomationException {
        IWindowAccessor window = getSwingWindowById(windowId);
        int dialogPanelSizeBefore = 0;
        boolean checkDialogPanelSize = false;

        // when you add item to the basket by pressing ENT button, some items takes few seconds to
        // display in the basket. i.e promotion items
        if (waitForPanelSizeUpdate && buttonLabel.equals(ENT_BUTTON_LABEL)) {
            dialogPanelSizeBefore = window.findComponent(getComponentId(RECEIPT_COMPONENT_NAME)).getDialogPanel().size();
            checkDialogPanelSize = true;
        }
        swingAppStepsProvider.pressButtonWithLabel(buttonLabel, windowId, getPosButtonComponentIdList(), timeout);

        // if item added to the basket, we need to wait until item is display in the basket
        if (checkDialogPanelSize) {
            if (!WaitForDialogPanelToChangeSize(dialogPanelSizeBefore, WAIT_DIALOG_PANEL_UPDATE)) {
                throw new AutomationException("Shopping basket is not updated within " + WAIT_DIALOG_PANEL_UPDATE);
            }
        }
    }

    private String getActiveWindowId() throws AutomationException {
        return getController().getActiveWindowTitle();
    }

    /**
     * Check basket size increased after adding an item to it
     *
     * @param dialogPanelSizeBefore
     * @param waitTime
     * @return boolean
     * @throws AutomationException
     */
    private boolean WaitForDialogPanelToChangeSize(int dialogPanelSizeBefore, long waitTime) throws AutomationException {
        IWindowAccessor window = getSwingWindow(TRANSACTION_BASKET_WINDOW_NAME);
        long checkUntil = System.currentTimeMillis() + waitTime;
        while (System.currentTimeMillis() < checkUntil) {
            waits();
            int dialogPanelSizeAfter = window.findComponent(getComponentId(RECEIPT_COMPONENT_NAME)).getDialogPanel().size();
            if (dialogPanelSizeBefore < dialogPanelSizeAfter) {
                return true;
            }
        }
        return false;
    }
}
