public with sharing class OFSHospitalLayoutController {
    private static integer SECTIONMAX = 10;
    private static integer REPORTMAX = 6;
    private static integer FIELDMAX = 100;
    public static integer RELATIONMAX = 10;
    public Integer pageNo { get; private set; }
    public String blockTitle { get; private set; }
    public Account acc {get; set;}
    private String accid;                    // 自分のid
    private String hpid;                     // 病院のid
    public List<Id> accIds { get; private set; }
    public List<SectionBean> sectionList { get; private set; }
    public Map<Id, String> accLabelMap { get; private set; }
    public Map<Id, Id> labelMapKeyMap {get; private set;}
    private OFSHospitalLayout__c layout;
    public List<SObject> asList { get; private set; }
    public List<String> title1 { get; private set; }
    public List<List<String>> columns1 { get; private set; }
    public List<String> column1 { get; private set; }
    public List<Integer> relationKeyList { get; private set; }
    public Map<Integer, RelationList> relationMap { get; private set; }             // リレーション定義
    public Map<Integer, List<SObject>> sobjectMap { get; private set; }             // 選択したリレーションのデータ
    public Integer index { get; set; }                                              // リレーションのindex
    public Boolean initFlag { get; private set; }
    //関連リストAssetのチェックボックス
    public Map<Id, Boolean> checkMap { get; set; }
    public Integer productCount {
        get {
            return checkMap == null ? 0 : checkMap.size();
        }
    }
    public List<Map<String, String>> selectedRptMapList { get; private set; }
    
    /**
     * Visaulforceから呼ばれるコンストラクタ
     */
    public OFSHospitalLayoutController(ApexPages.StandardController controller) {
        sectionList = new List<SectionBean>();
        accIds = new List<Id>();
        accLabelMap = new Map<Id, String>();
        labelMapKeyMap = new Map<Id, Id>();
        
        layout = new OFSHospitalLayout__c();
        selectedRptMapList = new List<Map<String, String>>();
        relationKeyList = new List<Integer>();
        relationMap = new Map<Integer, RelationList>();
        sobjectMap = new Map<Integer, List<SObject>>();
        checkMap = new Map<Id, Boolean>();
    }
    
    public void init() {
        initFlag = true;
        index = 0;
        PageNo = 0;
        String idParam = ApexPages.currentPage().getParameters().get('id');
        if (String.isBlank(idParam)) {
            initFlag = false;
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'Cannot find id of account!'));
            return;
        }
        List<Account> queryAccs = [select Id, ParentId, Parent.Parent.RecordType.DeveloperName, Parent.RecordType.DeveloperName, RecordType.DeveloperName, RecordType.Name from Account where Id = :idParam];
        if (queryAccs.size() <= 0) {
            initFlag = false;
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'Cannot find id of account!'));
            return;
        } else {
            Account tempacc = queryAccs[0];
            // 診療科なら戦略科室を取得
            if ((tempacc.RecordType.DeveloperName == 'HP'||tempacc.Parent.RecordType.DeveloperName == 'HP') != true) {
                queryAccs = [select Id, ParentId, Parent.Parent.RecordType.DeveloperName, Parent.RecordType.DeveloperName, RecordType.DeveloperName, RecordType.Name from Account where Id = :tempacc.ParentId];
            }
            acc = queryAccs[0];
            accid = acc.id;
        }
        system.debug('rectype:'+acc.RecordType.DeveloperName);
        system.debug('Prectype:'+acc.Parent.RecordType.DeveloperName);
        system.debug('PPrectype:'+acc.Parent.Parent.RecordType.DeveloperName);
        
        List<Account> accList = null;
        if (acc.RecordType.DeveloperName == 'HP') {
            hpid = acc.Id;
            accIds.add(acc.Id);
            accLabelMap.put(acc.Id, '医院');
            labelMapKeyMap.put(acc.Id, acc.Id);
            accList = [select Id, Department_Class_Label__c, RecordType.DeveloperName from Account where ParentId =: acc.Id
                       order by Department_Class_Label__c];
        //戦略科室
        } else {
            // TODO 販売店はまだ
            hpid = acc.ParentId;
            accIds.add(acc.ParentId);
            accLabelMap.put(acc.ParentId, '医院');
            labelMapKeyMap.put(acc.Id, acc.Id);
            accList = [select Id, Department_Class_Label__c, RecordType.DeveloperName from Account where ParentId =: acc.ParentId 
                       order by Department_Class_Label__c];
        } 
        
        if (accList != null) {
            for (Account dc : accList) {
                accIds.add(dc.Id);
                accLabelMap.put(dc.Id, dc.Department_Class_Label__c);
            }
        }

        Map<String, OFSHospitalLayout__c> m = OFSHospitalLayout__c.getAll();
        for (OFSHospitalLayout__c setting : m.values()) {
            if (acc.RecordType.DeveloperName == setting.recordType_devName__c) {
                layout = setting;
                blockTitle = setting.Name;
            }
        }
        
        if (layout.Id == null) {
            initFlag = false;
            ApexPages.addmessage(new ApexPages.message(ApexPages.severity.ERROR,'No custom settings about the account type!'));
            return;
        }
        Map<String, SectionBean> sectionMap = new Map<String, SectionBean>();
        for (Integer i = 1; i <= SECTIONMAX; i++) {
            String strI = 'section'+i+'__c';
            String sectionStr = String.valueOf(layout.get(strI));
            if (String.isBlank(sectionStr) == false) {
                SectionBean section = new SectionBean(sectionStr);
                if (i == 1) section.isTop = true;
                sectionList.add(section);
                sectionMap.put(section.id, section);
            }
        }
        
        List<List<String>> sectionApiList = new List<List<String>>();       // FIXME yu why not Set<String>、apiTempSet と重複しています。
        String jsonField = '';
        for (Integer i = 1; i <= FIELDMAX; i++) {
            String strI = 'field'+i+'__c';
            String jsonFieldtmp = String.valueOf(layout.get(strI));
            if (String.isBlank(jsonFieldtmp) == false) {
                jsonFieldtmp = jsonFieldtmp.trim();
                if (jsonFieldtmp.endsWith(' _')) {
                    jsonField += jsonFieldtmp.substring(0, jsonFieldtmp.length() - 2);
                    continue;
                } else {
                    jsonField += jsonFieldtmp;
                }
                SectionItem field = new SectionItem(jsonField, i);
                jsonField = '';                                 // 次を備えるため、'' にする
                SectionBean section = sectionMap.get(field.sectionId);
                if(section != null) {
                    sectionApiList.add(field.apiList);         // FIXME yu why not addAll
                    if(field.right) {
                        section.rightSectionList.add(field);
                    }else{
                        section.leftSectionList.add(field);
                    }
                }
            }
        }
        
        String soql = 'select Id, Name';
        //重複な項目を追加しないためのセット
        Set<String> apiTempSet = new Set<String>();
        apiTempSet.add('Id');
        apiTempSet.add('Name');
        for (List<String> apiList : sectionApiList) {
            for (String apiStr : apiList) {
                if (String.isBlank(apiStr) == false && apiTempSet.contains(apiStr) == false) {
                    soql += ', ';
                    soql += apiStr;
                    apiTempSet.add(apiStr);
                }
            }
        }
        
        soql += ' from Account where Id = :accid';
        List<Account> accQueryResults = Database.query(soql);
        acc = accQueryResults[0];
        
        for (integer i = 1; i <= REPORTMAX; i++) {
            Map<String, String> selectedRptMap = new Map<String, String>();
            selectedRptMap.put('Idx', String.valueOf(i));
            if (layout.get('report'+ String.valueOf(i) + '__c') != null) {
                selectedRptMap.put('Name', String.valueOf(layout.get('report'+ String.valueOf(i) +'_Name__c')));
                selectedRptMap.put('Report', String.valueOf(layout.get('report'+ String.valueOf(i) + '__c')));
                if (String.valueOf(layout.get('report' + String.valueOf(i) + '_Column__c')) != null) {
                    selectedRptMap.put('Column', '' + String.valueOf(layout.get('report' + String.valueOf(i) + '_Column__c')));
                } else {
                    selectedRptMap.put('Column', 'false');
                }
                selectedRptMapList.add(selectedRptMap);
            }
        }
        
        for (integer i = 1; i <= RELATIONMAX; i++) {
            String relationStr = 'relationList'+i+'__c';
            String jsonRelation = String.valueOf(layout.get(relationStr));
            if (String.isBlank(jsonRelation) == false) {
                RelationList relaList = new RelationList(jsonRelation, accid, hpid);
                relationKeyList.add(i);
                relationMap.put(i, relaList);
            }
        }
        
        //Asset関連リストを空の初期化
        RelationList initRelationList = new RelationList('');
        relationMap.put(0, initRelationList);
        List<SObject> initAsList = new List<SObject>();
        sobjectMap.put(0, initAsList);
    }
    
    public void prePage() {
        if (pageNo > 0) {
            pageNo += -1;
        }
    }
    
    public void nextPage() {
        if (pageNo < Math.floor(selectedRptMapList.size()/3) ) {
            pageNo += 1;
        }
        
    }
    
    public void showList() {
        //index = cnt;
        system.debug('------------index:' + index);
        RelationList re = relationMap.get(index);
        if (re.mode == 'relation') {
            String tmpAccid = (re.id15 == true) ? accid.substring(0, 15) : accid;
            String soqlAs = 'select Id';
            for (String s : re.column) {
                soqlAs += ',' + s;
            }
            soqlAs += ' from '+ re.obj + ' where ' + re.conditionColumn + ' = :tmpAccid';
            asList = Database.query(soqlAs);
        
            sobjectMap.put(index,asList);
        
            //Assetなら、チェックボックスを設定
            if (re.obj == 'Asset') {
                for (SObject a :asList) {
                    checkMap.put(a.Id, false);
                }
            }
        }
    }
    
    
    public PageReference save() {
        Savepoint sp = Database.setSavepoint();
        try {
            update acc;
        }
        catch (Exception e) {
            Database.rollback(sp);
            ApexPages.addMessages(e);
        }
        return null;
    }
    
    
    public PageReference irCreate() {
        String irStr = '/apex/OFSInsReportLayout?wv=1&hpid=';
        irStr += acc.id;
        
        //recordTypeの対応
        
        if (accLabelMap.get(acc.Id) == '医院') irStr += '&rt=EndoscopeSystem';
        else if (accLabelMap.get(acc.Id) == '呼吸科') irStr += '&rt=test2';
        
        irStr += '&asset_ids=';
        for (SObject a :asList) {
            if (checkMap.get(a.Id) == true) {
                irStr += a.Id + '_';
            }
        }
        irStr = irStr.removeEnd('_');
        PageReference ir = new PageReference(irStr);
        return ir;
    }

    class SectionBean {
        public String title { get; private set; }
        public Integer column { get; private set; }
        public Boolean showHeader{ get; private set; }
        public String id { get; private set; }
        public List<SectionItem> leftSectionList { get; private set; }
        public List<SectionItem> rightSectionList { get; private set; }
        public Boolean isTop { get; set; }
        
        // leftとrightのサイズ違う場合、最後空のSectionItemを追加
        public List<SectionItem> getSectionItemList() {
            List<SectionItem> sectionItemList = new List<SectionItem>();
            Integer lCnt = leftSectionList.size();
            Integer rCnt = rightSectionList.size();
            if (column == 1) {
                return leftSectionList;
            } else {
                for (Integer i = 0; i < Math.max(lCnt, rCnt); i++) {
                    if (lCnt <= i) {
                        sectionItemList.add(new SectionItem());
                    } else {
                        sectionItemList.add(leftSectionList[i]);
                    }
                    if (rCnt <= i) {
                        sectionItemList.add(new SectionItem());
                    } else {
                        sectionItemList.add(rightSectionList[i]);
                    }
                }
            }
            return sectionItemList;
        }
        
        public SectionBean(String jsonSection){
            leftSectionList = new List<SectionItem>();
            rightSectionList = new List<SectionItem>();
            Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonSection);
            id =  String.valueOf(m.get('id'));
            title = '';
            if (m.get('title') != null) {
                title = String.valueOf(m.get('title'));
            }
            column = 1;
            if (m.get('column') != null) {
                column = Integer.valueOf(m.get('column'));
            }
            showHeader = true;
            if (m.get('showHeader') != null) {
                showHeader = Boolean.valueOf(m.get('showHeader'));
            }
        }
    }

    public class SectionItem {
        public String api { get; private set; }
        public List<String> apiList { get; private set; }
        public Map<String, String> apiLabelMap { get; private set; }
        public Map<String, String> apiStyleMap { get; private set; }
        public Map<String, Boolean> apiRequireMap { get; private set; }
        public Map<String, Boolean> apiInputMap { get; private set; }
        public String sectionId { get; private set; }
        public Boolean right { get; private set; }
        public Boolean isDummy { get; private set; }
        public Boolean isCustomize { get; private set; }
        public String customizeLable { get; private set; }
        public Boolean isCustomizeStyle { get; private set; }
        public Boolean isInput { get; private set; }
        public Boolean isRequired { get; private set; }
        public String width { get; private set; }
        public String height { get; private set; }
        public Integer index { get; private set; }

        public SectionItem() {
            isDummy = true;
        }
        public SectionItem(String jsonField, Integer idx) {
            isDummy = false;
            Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonField);
            apiList = new List<String>();
            apiLabelMap = new Map<String, String>();
            apiStyleMap = new Map<String, String>();
            apiRequireMap = new Map<String, Boolean>();
            apiInputMap = new Map<String, Boolean>();
            index = idx;
            if (m.get('api') instanceof Map<String, Object>) {
                Map<String, Object> aMap = (Map<String, Object>) m.get('api');
                if (aMap.get('columns') instanceof List<Object> && aMap.get('lables') instanceof List<Object>) {
                    List<Object> cList = (List<Object>)aMap.get('columns');
                    List<Object> lList = (List<Object>)aMap.get('lables');
                    List<Object> sList = new List<Object>();
                    List<Object> rList = new List<Object>();
                    List<Object> iList = new List<Object>();
                    if (aMap.get('styles') instanceof List<Object>) {
                        sList = (List<Object>)aMap.get('styles');
                        isCustomizeStyle = true;
System.debug('isCustomizeStyle=' + aMap);
                    } else {
                        isCustomizeStyle = false;
                    }
                    if (aMap.get('require') instanceof List<Object>) {
                        rList = (List<Object>)aMap.get('require');
                    }
                    if (aMap.get('isInput') instanceof List<Object>) {
                        iList = (List<Object>)aMap.get('isInput');
                    }
                    if (cList.size() != lList.size()) {
                        ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.ERROR,'Error: Invalid CustomSettings about columns and labels.');
                        ApexPages.addMessage(myMsg);
                        return;
                    }

                    for (Integer i = 0; i < cList.size(); i++) {
                        apiList.add(String.valueOf(cList[i]));
                        apiLabelMap.put(String.valueOf(cList[i]), String.valueOf(lList[i]));
                        if (sList != null && sList.size() > 0 && sList[i] != null) {
                            apiStyleMap.put(String.valueOf(cList[i]), String.valueOf(sList[i]));
                        } else {
                            apiStyleMap.put(String.valueOf(cList[i]), '');
                        }
                        // require
                        if (rList != null && rList.size() > 0 && rList[i] != null) {
                            apiRequireMap.put(String.valueOf(cList[i]), Boolean.valueOf(rList[i]));
                        } else {
                            apiRequireMap.put(String.valueOf(cList[i]), false);
                        }
                        // input
                        if (iList != null && iList.size() > 0 && iList[i] != null) {
                            apiInputMap.put(String.valueOf(cList[i]), Boolean.valueOf(iList[i]));
                        } else {
                            apiInputMap.put(String.valueOf(cList[i]), false);
                        }
                    }
                } else {
                    apiList.add(String.valueOf(aMap.get('columns')));
                    apiLabelMap.put(String.valueOf(aMap.get('columns')), String.valueOf(aMap.get('lables')));
                    apiStyleMap.put(String.valueOf(aMap.get('columns')), '');
                }

                isCustomize = true;
            } else {
                api = String.valueOf(m.get('api'));
                apiList.add(String.valueOf(api));
                isCustomize = false;
            }
            if (m.get('lable') != null) {
                customizeLable = String.valueOf(m.get('lable'));
                if (String.isBlank(customizeLable)) {
                    customizeLable = null;
                }
            }
            if (m.get('width') != null) {
                width = String.valueOf(m.get('width'));
            }
            if (m.get('height') != null) {
                height = String.valueOf(m.get('height'));
            }
            sectionId = String.valueOf(m.get('sectionId'));
            if (m.get('right')!=null) {
                right = Boolean.valueOf(m.get('right'));
            } else {
                right = false;
            }
            if (m.get('isInput')!=null) {
                isInput = Boolean.valueOf(m.get('isInput'));
            } else {
                isInput = false;
            }
            if (m.get('require')!=null) {
                isRequired = Boolean.valueOf(m.get('require'));
            } else {
                isRequired = false;
            }
        }
/*
        public Boolean isRight() {
            return this.right;
        }
        public String getSectionId() {
            return this.sectionId;
        }
        public String getApi() {
            return this.api;
        }
        public List<String> getApiList(){
            return this.apiList;
        }
*/
    }
    
    class RelationListColumn {
        public String column0 { get; private set; }
        public String column1 { get; private set; }
    }
    class RelationList {
        // mode:null,page,relation
        public String mode { get; set; }
        /*public List<String> title { get; set; }
        public List<List<String>> columns { get; set; }
        public List<String> column { get; set; }
        */
        // 项目set 字段标签
        public List<String> title { get; private set; }
        // 项目set 字段名
        public List<String> column;       // sql 側使う
        public List<RelationListColumn> columns { get; private set; }

        public String obj { get; set; }
        public String fieldSet { get; set; }            // 表示用の fieldSet 名 or pageのurl
        public String conditionColumn { get; set; }
        public String fieldSetLabel { get; private set; }
        public String soql { get; set; }
        
        public String top { get; set; }
        public String height { get; set; }
        
        public Boolean id15 { get; set; }
        
        public RelationList(String mode) {
            this.mode = mode;
        }
        public RelationList(String jsonRelation, String accidStr, String hpidStr) {
            this.mode = 'relation';
            id15 = false;
            
            Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonRelation);
            
            if (m.get('object') != null) {
                obj = String.valueOf(m.get('object'));
            }
            if (m.get('field_set') != null) {
                fieldSet = String.valueOf(m.get('field_set'));
            }
            if (m.get('condition_column') != null) {
                conditionColumn = String.valueOf(m.get('condition_column'));
            }
            if (m.get('top') != null) {
                top = String.valueOf(m.get('top'));
            } else {
                top = '30%';
            }
            if (m.get('height') != null) {
                height = String.valueOf(m.get('height'));
            } else {
                height = '380px';
            }
            if (m.get('id15') != null) {
                id15 = Boolean.valueOf(m.get('id15'));
            }
            // 一番最後の置いてください。
            if (m.get('page') != null) {
                this.mode = 'page';
                this.fieldSetLabel = this.obj;
                if (m.get('label') != null) {
                    this.fieldSetLabel = String.valueOf(m.get('label'));
                }
                // xudan 20150626 url paramに応じてIdをセット
                // hpid=:hpid⇒病院のidを使う、hpid=:id⇒自分のidを使う
                String urlStr = String.valueOf(m.get('page'));
                urlStr = urlStr.replace(':hpid', hpidStr);
                urlStr = urlStr.replace(':id', accidStr);
                fieldSet = urlStr;
                //fieldSet = String.valueOf(m.get('page')) + accidStr;
                return;
            }

            Map<String, Schema.FieldSet> fsMap = Schema.getGlobalDescribe().get(obj).getDescribe().fieldSets.getMap();
            
            Schema.FieldSet fs = fsMap.get(fieldSet);
            List<FieldSetMember> fsms = fs.getFields();
            fieldSetLabel = fs.getLabel();
            title = new List<String>();
            column = new List<String>();
            columns = new List<RelationListColumn>();
            for (FieldSetMember fsm : fsms) {
                title.add(fsm.getLabel());
                column.add(fsm.getFieldPath());
                List<String> fieldPathList = fsm.getFieldPath().split('\\.');
                RelationListColumn rlc = new RelationListColumn();
                columns.add(rlc);
                rlc.column0 = fieldPathList[0];
                if (fieldPathList.size() == 2) rlc.column1 = fieldPathList[1];
            }
            //aList = Database.query(soql);
        }
    }
}