/**
 * 权限管理
 */
import React from 'react'
import { Tabs, Layout, Row, Col, Input, Menu, Table, Checkbox, message, Button, Icon } from 'antd'
import { connect } from 'dva'
import Loading from '../common/loading/index'
import './index.less'
const { Sider, Header, Content } = Layout
const { Search } = Input
const { TabPane } = Tabs

class Authority extends React.Component {

    componentDidMount() {
        const { dispatch } = this.props;
        dispatch({ type: 'userGroup/fetchList' });
        dispatch({ type: 'user/fetchList' });
        dispatch({ type: 'dashboard/remoteMenuTree' });
    }

    checkIndeterminate = (treeList) => {
        const { authority } = this.props;
        const { dashboardList, dashboardFilterLabel } = authority;
        let arr = treeList && treeList.length > 0 ? [ ...treeList ] : null;
        for(let i = (arr ? (arr.length - 1) : 0); arr && i >= 0; i--) {
            let l = arr[i];
            if(l.type === 'menu') {
                l.children = this.checkIndeterminate(l.children);
                let checkedCount = l.children ? l.children.filter(c => c.type === 'dashboard').map(c => c.checked).filter(c => !!c).length : 0;
                l.indeterminate = l.children ? (checkedCount > 0 && checkedCount < l.children.filter(c => c.type === 'dashboard').length) : false;
                l.checked = l.children ? checkedCount !== 0 && checkedCount === l.children.filter(c => c.type === 'dashboard').length : false;

                if((!l.children || l.children.length === 0) && !!dashboardFilterLabel && l.name.toLowerCase().indexOf(dashboardFilterLabel.toLowerCase()) === -1) {
                    arr.splice(i, 1);
                }
            }else {
                l.children = null;
                l.indeterminate = false;
                l.checked = dashboardList.findIndex(d => d === l.code) > -1;

                if(!!dashboardFilterLabel && l.name.toLowerCase().indexOf(dashboardFilterLabel.toLowerCase()) === -1) {
                    arr.splice(i, 1);
                }
            }
        }
        return arr;
    }

    changeTab = (key) => {
        const { dispatch } = this.props;
        dispatch({ type: 'authority/setFields', fields: [
            { name: 'tabActiveKey', value: key },
            { name: 'menuSelectedKeys', value: [] },
            { name: 'dashboardList', value: [] },
        ] });
    }

    render() {
        const { dashboard, authority, userGroup, user, dispatch } = this.props;
        const { panelLoading, menuSelectedKeys, tabActiveKey, groupFilterLabel, userFilterLabel, dashboardFilterLabel, groupLimit, userLimit } = authority;
        
        const dashboardTreeList = this.checkIndeterminate(JSON.parse(JSON.stringify(dashboard.menuTree)));
        return <Layout className='layout-authority'>
            <Loading visible={panelLoading} />
            <Sider>
                <Tabs
                    className='tabs-authority'
                    type='card'
                    tabPosition='top'
                    defaultActiveKey="userGroup"
                    activeKey={tabActiveKey}
                    onChange={this.changeTab}
                >
                    <TabPane tab="用户组" key="userGroup" >
                        <MenuList
                            refName='group'
                            selectedKeys={menuSelectedKeys}
                            list={userGroup.list}
                            filterLabel={groupFilterLabel}
                            filterLabelChange={(val) => {
                                dispatch({ type: 'authority/setFields', fields: [
                                    { name: 'groupLimit', value: 30 },
                                    { name: 'groupFilterLabel', value: val },
                                ] })
                            }}
                            displayField='name'
                            valueField='code'
                            onItemClick={(item) => {
                                dispatch({ type: 'authority/batchActions', actions: [
                                    { type: 'setFields', fields: [
                                        { name: 'menuSelectedKeys', value: [item.code] },
                                    ] },
                                    { type: 'fetchDashboardTree', dtype: '0', code: item.code }
                                ] });
                            }}
                            limit={groupLimit}
                            onScrollToBottom={() => {
                                window.clearTimeout(this.groupLimitKey);
                                this.groupLimitKey = window.setTimeout(() => {
                                    dispatch({ type: 'authority/setFields', fields: [
                                        { name: 'groupLimit', value: (groupLimit + 10 > userGroup.list.length ? userGroup.list.length : groupLimit + 10) },
                                    ] })
                                }, 500);
                            }}
                        />
                    </TabPane>
                    <TabPane tab="用户" key="user">
                        <MenuList
                            refName='user'
                            selectedKeys={menuSelectedKeys}
                            list={user.list}
                            filterLabel={userFilterLabel}
                            filterLabelChange={(val) => {
                                dispatch({ type: 'authority/setFields', fields: [
                                    { name: 'userLimit', value: 30 },
                                    { name: 'userFilterLabel', value: val },
                                ] })
                            }}
                            displayField='fullName'
                            valueField='code'
                            onItemClick={(item) => {
                                dispatch({ type: 'authority/batchActions', actions: [
                                    { type: 'setFields', fields: [ { name: 'menuSelectedKeys', value: [item.code] }, ]},
                                    { type: 'fetchDashboardTree', dtype: '1', code: item.code }
                                ] });
                            }}
                            limit={userLimit}
                            onScrollToBottom={() => {
                                window.clearTimeout(this.userLimitKey);
                                this.userLimitKey = window.setTimeout(() => {
                                    dispatch({ type: 'authority/setFields', fields: [
                                        { name: 'userLimit', value: (userLimit + 10 > user.list.length ? user.list.length : userLimit + 10) },
                                    ] })
                                }, 500);
                            }}
                        />
                    </TabPane>
                </Tabs>
            </Sider>
            <Content>
                <Header>
                    <Row className='search-bar' type='flex' justify='end'>
                        <Col className='search'>
                            <Button className='btn-refresh' onClick={() => {
                                dispatch({ type: 'authority/setField', name: 'panelLoading', value: true });
                                dispatch({ type: 'userGroup/fetchList', mandatory: true });
                                dispatch({ type: 'user/fetchList', mandatory: true });
                                dispatch({ type: 'dashboard/remoteMenuTree', mandatory: true }).then(() => {
                                    dispatch({ type: 'authority/setField', name: 'panelLoading', value: false });
                                });
                            }}>
                                <Icon type='sync'/>
                            </Button>
                            <Search
                                placehodler='请输入关键字'
                                value={dashboardFilterLabel}
                                onChange={e => {
                                    let value = e.target.value;
                                    dispatch({ type: 'authority/setFields', fields: [
                                        { name: 'dashboardFilterLabel', value },
                                    ] })
                                }}
                            />
                        </Col>
                    </Row>
                </Header>
                <DashBoardTree filterLabel={dashboardFilterLabel} dataSource={dashboardTreeList} onRowCheckChange={(record, checked) => {
                    if(!!menuSelectedKeys[0]) {
                        if(record.type === 'menu') {
                            let dashboardCodes = record.children.filter(c => c.type === 'dashboard').map(c => c.code)
                            dispatch({ type: 'authority/' + (checked ? 'addAll': 'removeAll'), dtype: tabActiveKey === 'user' ? 1 : 0, code: menuSelectedKeys[0], dashboardCodes });
                        }else {
                            dispatch({ type: 'authority/' + (checked ? 'add': 'remove'), dtype: tabActiveKey === 'user' ? 1 : 0, code: menuSelectedKeys[0], dashboardCode: record.code });
                        }
                    }else {
                        message.error('请先选择左侧用户组/用户');
                    }
                }}/>
            </Content>
        </Layout>
    }
}

class MenuList extends React.Component {
    componentDidMount() {
        this.addEvents();
    }

    addEvents = () => {
        const { refName } = this.props;
        let tabs = this['menuList-' + refName];
        let menu = tabs.getElementsByClassName('ant-menu')[0];
        menu.removeEventListener('scroll', this.onMenuScroll);
        menu.addEventListener('scroll', this.onMenuScroll);
    }

    onMenuScroll = (e) => {
        const { onScrollToBottom } = this.props;
        let target = e.target;
        if(target.scrollHeight - target.offsetHeight === target.scrollTop) { // 滚动到底了
            typeof onScrollToBottom === 'function' && onScrollToBottom()
        }
    }

    render() {
        const { refName, selectedKeys, list, filterLabel: pFilterLabel, filterLabelChange, displayField, valueField, onItemClick, limit } = this.props;
        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
        let filterLabel = (pFilterLabel || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号

        return <div ref={node => this['menuList-' + refName] = node} className='menu-list'>
            <div className='search-area'>
                <Search
                    value={pFilterLabel}
                    placeholder="请输入关键字"
                    onChange={e => {
                        let val = e.target.value + '';
                        typeof filterLabelChange === 'function' && filterLabelChange(val);
                    }}
                />
            </div>
            <Menu
                selectedKeys={selectedKeys}
            >
                {list.filter(l => {
                    let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
                    return ((l[displayField] || '').search(reg) !== -1 || l[valueField] === selectedKeys[0]);
                }).slice(0, limit).map(l => (
                    <Menu.Item key={l[valueField]+''} onClick={() => {
                        typeof onItemClick === 'function' && onItemClick(l);
                    }} >
                        <div className='item-title'>
                            <span className='label' title={l[displayField]}>
                                { filterLabel ?
                                    ((l[displayField] || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
                                        return (
                                            fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
                                            <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
                                            fragment
                                        )
                                    }
                                    )) : <span>{l[displayField]}</span>
                                }
                            </span>
                        </div>
                    </Menu.Item>
                ))}
            </Menu>
        </div>
    }
}

class DashBoardTree extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            tableScrollHeight: 0,
            allExpanded: false,
            expandedRowKeys: [],
        }
    }

    componentDidMount() {
        this.tableSize();
        window.addEventListener('resize', this.tableSize);
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.tableSize);
    }

    tableSize = () => {
        const content = document.getElementsByClassName('content-setting')[0];
        const tableWrapper = content.getElementsByClassName('ant-table-wrapper')[0];
        const tableContent = tableWrapper.getElementsByClassName('ant-spin-nested-loading')[0];
        const tableHeader = tableContent.querySelector('thead');
        const padding = tableContent.getBoundingClientRect().top - tableWrapper.getBoundingClientRect().top;
        // 容器高度 - padding * 2 - 表头高度 - 边框线
        let tableScrollHeight = tableWrapper.offsetHeight - padding * 2 - tableHeader.offsetHeight - 2;
        this.setState({
            tableScrollHeight,
        });
    }

    onExpandedRowsChange = (expandedRowKeys) => {
        this.setState({
            expandedRowKeys
        });
    }

    onExpandAll = () => {
        const { dataSource } = this.props;
        let expandedRowKeys = this.getAllKeys(dataSource);
        this.setState({
            allExpanded: true,
            expandedRowKeys
        });
    }

    collapseAll = () => {
        this.setState({
            allExpanded: false,
            expandedRowKeys: []
        });
    }

    getAllKeys = (list) => {
        let keyArr = [];
        for(let i = 0;!!list && i < list.length; i++) {
            if(!!list[i].children) {
                keyArr.push(list[i].key);
                keyArr = keyArr.concat(this.getAllKeys(list[i].children, keyArr));
            }
        }
        return keyArr
    }

    render() {
        const { dataSource, onRowCheckChange, filterLabel } = this.props;
        const { tableScrollHeight, expandedRowKeys, allExpanded } = this.state;
        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
        const columns = [{
            title: <span><Icon type='menu' title='展开/折叠全部' style={{ marginRight: '4px' }} onClick={allExpanded ? this.collapseAll : this.onExpandAll}/>报表目录</span>,
            dataIndex: 'name',
            key: 'name',
            width: '80%',
            render: (text, record) => {
                return <span style={{ fontWeight: record.type === 'dashboard' ? 'bold' : 'normal' }}>
                    {
                        filterLabel ?
                        ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
                            return (
                                fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
                                <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
                                fragment
                            )
                        }
                        )) : text
                    }
                </span>
            }
        }, {
            title: '查看',
            dataIndex: 'view',
            key: 'view',
            width: '20%',
            render: (text, record, index) => {
                return <Checkbox disabled={record.type === 'menu' && (!record.children || record.children.filter(c => c.type === 'dashboard').length === 0)} indeterminate={record.indeterminate} checked={record.checked} onChange={(e) => {
                    let checked = e.target.checked;
                    typeof onRowCheckChange === 'function' && onRowCheckChange(record, checked);
                }} />
            }
        }];

        return <Table columns={columns} dataSource={dataSource} scroll={{x: false, y: tableScrollHeight}}
            size='small'
            pagination={false}
            expandedRowKeys={expandedRowKeys}
            onExpandedRowsChange={this.onExpandedRowsChange}
        />
    }
}

export default connect(({ present: { authority, userGroup, user, dashboard } }) => ({ authority, userGroup, user, dashboard }))(Authority)
