/*
 * Copyright 2014 by SCSK Corporation.
 * 
 * This file is part of PrimeCloud Controller(TM).
 * 
 * PrimeCloud Controller(TM) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * 
 * PrimeCloud Controller(TM) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PrimeCloud Controller(TM). If not, see <http://www.gnu.org/licenses/>.
 */
package jp.primecloud.auto.ui;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import jp.primecloud.auto.common.status.ComponentInstanceStatus;
import jp.primecloud.auto.common.status.InstanceStatus;
import jp.primecloud.auto.entity.crud.AwsVolume;
import jp.primecloud.auto.entity.crud.AzureDisk;
import jp.primecloud.auto.entity.crud.CloudstackVolume;
import jp.primecloud.auto.entity.crud.ComponentType;
import jp.primecloud.auto.entity.crud.OpenstackVolume;
import jp.primecloud.auto.entity.crud.VcloudDisk;
import jp.primecloud.auto.entity.crud.VmwareDisk;
import jp.primecloud.auto.service.ComponentService;
import jp.primecloud.auto.service.dto.ComponentDto;
import jp.primecloud.auto.service.dto.ComponentInstanceDto;
import jp.primecloud.auto.service.dto.ImageDto;
import jp.primecloud.auto.service.dto.InstanceDto;
import jp.primecloud.auto.ui.util.BeanContext;
import jp.primecloud.auto.ui.util.ContextUtils;
import jp.primecloud.auto.ui.util.IconUtils;
import jp.primecloud.auto.ui.util.Icons;
import jp.primecloud.auto.ui.util.ViewContext;
import jp.primecloud.auto.ui.util.ViewProperties;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;

import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.Form;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Table;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

/**
 * <p>
 * サーバ作成(編集)画面からサービスの割当ボタンを押したときのサービス割当画面を生成します。
 * </p>
 *
 */
@SuppressWarnings("serial")
public class WinServerAttachService extends Window {

    private InstanceDto instance;

    private ImageDto image;

    private List<Long> selectedComponentNos;

    private AttachService attachService;

    private List<ComponentDto> components;

    public WinServerAttachService(InstanceDto instance, ImageDto image, List<Long> selectedComponentNos) {
        this.instance = instance;
        this.image = image;
        this.selectedComponentNos = selectedComponentNos;
    }

    @Override
    public void attach() {
        // モーダルウインドウ
        setIcon(Icons.ADD.resource());
        setCaption(ViewProperties.getCaption("window.winServerAttachService"));
        setModal(true);
        setWidth("500px");

        VerticalLayout layout = (VerticalLayout) getContent();
        layout.setMargin(true, true, false, true);
        layout.setSpacing(false);

        // Form
        Form form = new Form();

        // サーバ名
        TextField serverNameField = new TextField(ViewProperties.getCaption("field.serverName"));
        if (instance != null) {
            serverNameField.setValue(instance.getInstance().getInstanceName());
        } else {
            serverNameField.setValue(ViewProperties.getCaption("field.newServer"));
        }
        serverNameField.setReadOnly(true);
        form.getLayout().addComponent(serverNameField);

        attachService = new AttachService(instance, image, selectedComponentNos);
        form.getLayout().addComponent(attachService);
        layout.addComponent(form);

        // 下部のバー
        HorizontalLayout bottomLayout = new HorizontalLayout();
        bottomLayout.setSpacing(true);
        bottomLayout.setMargin(false, false, true, false);
        layout.addComponent(bottomLayout);
        layout.setComponentAlignment(bottomLayout, Alignment.BOTTOM_RIGHT);

        // OKボタン
        Button okButton = new Button(ViewProperties.getCaption("button.ok"));
        okButton.setDescription(ViewProperties.getCaption("description.ok"));
        okButton.addListener(new Button.ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                addButtonClick(event);
            }
        });
        okButton.setClickShortcut(KeyCode.ENTER); // [Enter]でokButtonクリック
        okButton.focus();
        bottomLayout.addComponent(okButton);

        // Cancelボタン
        Button cancelButton = new Button(ViewProperties.getCaption("button.cancel"));
        cancelButton.setDescription(ViewProperties.getCaption("description.cancel"));
        cancelButton.addListener(new Button.ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                close();
            }
        });
        bottomLayout.addComponent(cancelButton);

        // サービス情報を表示
        loadData();
        attachService.show(components);
    }

    private class AttachService extends Table {

        private final String COLUMN_HEIGHT = "32px";

        private InstanceDto instance;

        private ImageDto image;

        private List<Long> selectedComponentNos;

        private List<ComponentDto> components;

        public AttachService(InstanceDto instance, ImageDto image, List<Long> selectedComponentNos) {
            this.instance = instance;
            this.image = image;
            this.selectedComponentNos = (selectedComponentNos == null) ? new ArrayList<Long>() : selectedComponentNos;
        }

        @Override
        public void attach() {
            // テーブル基本設定
            setColumnHeaderMode(Table.COLUMN_HEADER_MODE_EXPLICIT);
            addStyleName("win-server-attach-service");
            setCaption(ViewProperties.getCaption("table.serverAttachServices"));
            setSortDisabled(true);
            setMultiSelect(false);
            setImmediate(false);
            setVisible(true);
            setWidth("100%");
            setPageLength(5);

            // カラム設定
            addContainerProperty("check", CheckBox.class, new CheckBox());
            addContainerProperty("componentName", Label.class, new Label());
            addContainerProperty("status", Label.class, new Label());
            setColumnExpandRatio("componentName", 100);
            String[] headers = { "", ViewProperties.getCaption("field.serviceName"),
                    ViewProperties.getCaption("field.serviceStatus") };
            setColumnHeaders(headers);

            // テーブルのカラムに対してStyleNameを設定
            setCellStyleGenerator(new StandardCellStyleGenerator() {
                @Override
                public String getStyle(Object itemId, Object propertyId) {
                    String style = super.getStyle(itemId, propertyId);

                    CheckBox checkBox = getCheckBox(getItem(itemId));

                    // チェックボックスが選択されていれば行の色を変える
                    if (checkBox.booleanValue()) {
                        style += " v-selected";
                    }
                    // チェックボックスが無効なら色を変更する
                    if (!checkBox.isEnabled()) {
                        style += " v-disabled";
                    }

                    return style;
                }
            });

            // 行が選択された場合、行のチェックボックスの値を変える
            addListener(new ItemClickListener() {
                @Override
                public void itemClick(ItemClickEvent event) {
                    CheckBox checkBox = getCheckBox(event.getItem());
                    if (checkBox.isEnabled()) {
                        checkBox.setValue(!checkBox.booleanValue());
                    }
                }
            });
        }

        public void show(List<ComponentDto> components) {
            this.components = components;

            removeAllItems();

            if (components == null) {
                return;
            }

            for (ComponentDto component : components) {
                // チェックボックス
                CheckBox checkBox = new CheckBox();
                checkBox.setImmediate(true);
                checkBox.setEnabled(false);
                if (selectedComponentNos.contains(component.getComponent().getComponentNo())) {
                    checkBox.setValue(true);
                } else {
                    checkBox.setValue(false);
                }

                checkBox.addListener(new Property.ValueChangeListener() {
                    @Override
                    public void valueChange(Property.ValueChangeEvent event) {
                        // チェックボックスの有効/無効を制御
                        changeCheckEnabled();

                        // テーブル再描画
                        requestRepaint();
                    }
                });

                // サービス名
                String serviceName = component.getComponent().getComponentName();
                if (StringUtils.isNotEmpty(component.getComponent().getComment())) {
                    serviceName = component.getComponent().getComment() + "\n[" + serviceName + "]";
                }
                Label serviceNameLabel = new Label(serviceName, Label.CONTENT_PREFORMATTED);
                serviceNameLabel.setHeight(COLUMN_HEIGHT);

                // ステータス
                String status = null;
                if (instance != null) {
                    for (ComponentInstanceDto componentInstance : instance.getComponentInstances()) {
                        if (componentInstance.getComponentInstance().getComponentNo()
                                .equals(component.getComponent().getComponentNo())) {
                            status = componentInstance.getComponentInstance().getStatus();
                            break;
                        }
                    }
                }
                if (StringUtils.isEmpty(status)) {
                    status = "Stopped";
                } else {
                    status = StringUtils.capitalize(StringUtils.lowerCase(status));
                }

                Icons statusIcon = Icons.fromName(status);
                Label statusLabel = new Label(IconUtils.createImageTag(getApplication(), statusIcon, status),
                        Label.CONTENT_XHTML);
                statusLabel.setHeight(COLUMN_HEIGHT);

                addItem(new Object[] { checkBox, serviceNameLabel, statusLabel },
                        component.getComponent().getComponentNo());
            }

            changeCheckEnabled();
        }

        @Override
        @SuppressWarnings("unchecked")
        public Collection<Long> getItemIds() {
            return (Collection<Long>) super.getItemIds();
        }

        public List<Long> getSelectedValues() {
            List<Long> componentNos = new ArrayList<Long>();
            for (Long componentNo : getItemIds()) {
                CheckBox checkBox = getCheckBox(getItem(componentNo));
                if (checkBox.booleanValue()) {
                    componentNos.add(componentNo);
                }
            }

            return componentNos;
        }

        private CheckBox getCheckBox(Item item) {
            return (CheckBox) item.getItemProperty("check").getValue();
        }

        private void changeCheckEnabled() {
            // チェックボックスの有効/無効を制御する
            Set<Long> disableComponentNos = new HashSet<Long>();

            // 利用可能でないサービスは無効
            List<Long> componentTypeNos = new ArrayList<Long>();
            for (ComponentType componentType : image.getComponentTypes()) {
                componentTypeNos.add(componentType.getComponentTypeNo());
            }
            for (ComponentDto component : components) {
                if (!componentTypeNos.contains(component.getComponent().getComponentTypeNo())) {
                    disableComponentNos.add(component.getComponent().getComponentNo());
                }
            }

            if (instance != null) {
                // 割り当て済みでStoppedでないサービスは無効
                for (ComponentInstanceDto componentInstance : instance.getComponentInstances()) {
                    for (ComponentDto component : components) {
                        if (componentInstance.getComponentInstance().getComponentNo()
                                .equals(component.getComponent().getComponentNo())) {
                            ComponentInstanceStatus status = ComponentInstanceStatus
                                    .fromStatus(componentInstance.getComponentInstance().getStatus());
                            if (status != ComponentInstanceStatus.STOPPED) {
                                disableComponentNos.add(component.getComponent().getComponentNo());
                            }
                            break;
                        }
                    }
                }

                // ディスクがアタッチされているサービスは無効
                if (instance.getAwsVolumes() != null) {
                    for (AwsVolume awsVolume : instance.getAwsVolumes()) {
                        if (StringUtils.isNotEmpty(awsVolume.getInstanceId())) {
                            disableComponentNos.add(awsVolume.getComponentNo());
                        }
                    }
                }
                if (instance.getCloudstackVolumes() != null) {
                    for (CloudstackVolume cloudstackVolume : instance.getCloudstackVolumes()) {
                        if (StringUtils.isNotEmpty(cloudstackVolume.getInstanceId())) {
                            disableComponentNos.add(cloudstackVolume.getComponentNo());
                        }
                    }
                }
                if (instance.getVmwareDisks() != null) {
                    for (VmwareDisk vmwareDisk : instance.getVmwareDisks()) {
                        if (BooleanUtils.isTrue(vmwareDisk.getAttached())) {
                            disableComponentNos.add(vmwareDisk.getComponentNo());
                        }
                    }
                }
                if (instance.getVcloudDisks() != null) {
                    for (VcloudDisk vcloudDisk : instance.getVcloudDisks()) {
                        if (BooleanUtils.isTrue(vcloudDisk.getAttached())) {
                            if (InstanceStatus
                                    .fromStatus(instance.getInstance().getStatus()) != InstanceStatus.STOPPED) {
                                disableComponentNos.add(vcloudDisk.getComponentNo());
                            }
                        }
                    }
                }
                if (instance.getAzureDisks() != null) {
                    for (AzureDisk azureDisk : instance.getAzureDisks()) {
                        if (StringUtils.isNotEmpty(azureDisk.getInstanceName())) {
                            if (InstanceStatus
                                    .fromStatus(instance.getInstance().getStatus()) != InstanceStatus.STOPPED) {
                                disableComponentNos.add(azureDisk.getComponentNo());
                            }
                        }
                    }
                }
                if (instance.getOpenstackVolumes() != null) {
                    for (OpenstackVolume openstackVolume : instance.getOpenstackVolumes()) {
                        if (StringUtils.isNotEmpty(openstackVolume.getInstanceId())) {
                            if (InstanceStatus
                                    .fromStatus(instance.getInstance().getStatus()) != InstanceStatus.STOPPED) {
                                disableComponentNos.add(openstackVolume.getComponentNo());
                            }
                        }
                    }
                }
            }

            // 同一レイヤのサービス種類が選択されているサービスは無効
            for (ComponentDto component : components) {
                CheckBox checkBox = getCheckBox(getItem(component.getComponent().getComponentNo()));
                if (checkBox.booleanValue()) {
                    for (ComponentDto component2 : components) {
                        if (component.getComponent().getComponentNo()
                                .equals(component2.getComponent().getComponentNo())) {
                            continue;
                        }

                        if (component.getComponentType().getLayer().equals(component2.getComponentType().getLayer())) {
                            disableComponentNos.add(component2.getComponent().getComponentNo());
                        }
                    }
                }
            }

            // 有効/無効を反映
            for (Long componentNo : getItemIds()) {
                CheckBox checkBox = getCheckBox(getItem(componentNo));
                if (disableComponentNos.contains(componentNo)) {
                    checkBox.setEnabled(false);
                } else {
                    checkBox.setEnabled(true);
                }
            }
        }
    }

    private void loadData() {
        // 全てのサービス情報を取得
        ComponentService componentService = BeanContext.getBean(ComponentService.class);
        components = componentService.getComponents(ViewContext.getFarmNo());
    }

    private void addButtonClick(ClickEvent event) {
        // 選択したサービスの番号を取得
        List<Long> componentNos = attachService.getSelectedValues();

        // 選択したサービス追加したサービスの番号をセッションに格納
        ContextUtils.setAttribute("componentNos", componentNos);

        // 画面を閉じる
        close();
    }

}