<!DOCTYPE html>
<html>

<head>
    <script>
        console.clear()
    </script>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.0/semantic.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"></script>
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.2.0/redux-thunk.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/json-schema-faker/0.5.0-rc13/json-schema-faker.js"></script>
    <script src="./schema.js"></script>
    <link rel="stylesheet" href="./style.css">
    <script src="./store.js"></script>
</head>

<body>
    <div class="ui container">
        <div class="ui raised segment">
            <div id="main"></div>
        </div>
    </div>
    <script src="script.js"></script>
</body>

</html>
// Code goes here
const {Provider, connect} = ReactRedux;
const {createStore, applyMiddleware} = Redux;
const thunk = ReduxThunk.default;

const store = Redux.createStore(rootReducer, initialState, applyMiddleware(thunk));

const sortDirs = {
    'asc': 'desc',
    'desc': 'asc'
}

const sortFactors = {
    'asc': -1,
    'desc': 1
}

const ColumnHeader = ({column, sort, onclick}) => {
    const sortClass = sort.by === column.name ? 'sorted-' + sort.dir : '';
    return <th
            className={'sortable ' + sortClass}
            onClick={onclick}>
                {column.display}
            </th>;
}

const ItemRow = ({columns, item}) => {
    return (
        <tr>{columns.map((c,i) => {
            const className = c.name === 'distance' ? (item[c.name] > 250 ? 'positive': 'negative') : '';
            return <td key={c.name} className={className}>{item[c.name]}</td>
        })}
        </tr>
    );
}

const ColumnFilter = ({columns, onchange}) => {
    return (
      <tr>{columns.map(c => {
          return <td key={c.name}><div className="ui mini input">
          <input type="search" onChange={onchange}/>
          </div>
          </td>
      })}</tr>  
    );
}

class Items extends React.Component {

    componentDidMount() {
        this.props.fetchData();
    }
    
    sortByColumn(col) {
        const {items, sort} = this.props;
        this.props.sortItems(items, sort, col);
    }
    
    onFilterInput() {
        console.log('todo');
    }
    
    render() {
        const {items, columns, sort} = this.props;
        
        const headers = columns.map(c => 
            <ColumnHeader
                key={c.name}
                column={c}
                sort={sort}
                onclick={() => this.sortByColumn(c)}
            />
        );
        
        const rows = items.map(i => <ItemRow
                                        item={i}
                                        columns={columns}
                                        key={i.address}
                                    />
                                );
        
        const filters = <ColumnFilter columns={columns} onchange={this.onFilterInput}/>
        
        return (
            <table className="ui compact selectable table">
                <thead>
                <tr>{headers}</tr>
                </thead>
                <tbody className="filters">
                {filters}
                </tbody>
                <tbody>{rows}</tbody>
            </table>
            )
    }
}

const mapStateToProps = (state) => {
    return {
        items: state.items,
        columns: state.columns,
        sort: state.sort
    };
};


const mapDispatchToProps = (dispatch) => {
    return {
        fetchData: (url) => dispatch(itemsFetchData(url)),
        sortItems: (items, sortBy, col) => dispatch(itemsSort(items, sortBy, col))
    };
};

const ItemsTable = connect(mapStateToProps, mapDispatchToProps)(Items);

const App = () => <Provider store={store}><ItemsTable/></Provider>

ReactDOM.render(<App/>, $("#main")[0]);
/* Styles go here */

.ui.table > thead > tr > th {
    cursor: pointer;
    &:hover {
        background-color: #e6e6e6;
    }
}

th.sortable.sorted-asc:before {
    content: '\f0dd';
}

th.sortable.sorted-desc:before {
    content: '\f0de';
}

th.sortable:before {
    font-family: "Icons";
    margin-right: .3em;
    content: '\f0dc';
}

th:not(.sorted-asc):not(.sorted-desc):before {
    opacity: .2;
}


#main {height: calc(100vh - 100px);}
table {
    display: flex;
    flex-flow: column;
    height: 100%;
    width: 100%;
}
table thead {
    /* head takes the height it requires, 
    and it's not scaled when table is resized */
    flex: 0 0 auto;
    width: calc(100% - 0.9em);
}
table tbody.filters {
    flex: 0 0 auto;
    overflow-y: auto;
}
table tbody {
    /* body takes all the remaining available space */
    flex: 1 1 auto;
    display: block;
    overflow-y: auto;
}
table tbody tr {
    width: 100%;
}
table thead, table tbody tr {
    display: table;
    table-layout: fixed;
}

tbody.filters .ui.input > input {
    padding: .3em;
}
const schema = {
    "id": "User",
    "type": "array",
    "minItems": 100,
    "maxItems": 500,
    "items": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string",
                "faker": "name.firstName",
                "minLength": 3,
                "unique": true
            },
            "address": {
                "type": "string",
                "faker": "address.streetAddress"
            },
            "distance": {
                "type": "integer",
                "minimum": 1,
                "maximum": 500
            },
            "group": {
                "type": "string",
                "enum": ["A", "B", "C"]
            }
        },
        "required": [
            "name",
            "address",
            "distance",
            "group"
        ]
    }
}

JSONSchemaFaker.extend('faker', () => faker);
const {combineReducers} = Redux;

const initialState = {
    items: [],
    columns: [{name: "name", display: "Name"}, {name: "address", display: "Address"}, {name: "distance", display: "Distance"}, {name:"group", display: "Group"}],
    sort: {by: '', dir: 'asc'}
}

function items(state=[], action) {
    switch (action.type) {
        case 'ITEMS_FETCH_DATA_SUCCESS':
            return action.items;
        default:
            return state;
    }
}

function columns(state=initialState.columns, action) {
    return state;
}

function sort(state=initialState.sort, action) {
    switch (action.type) {
        case 'SET_SORT':
            return action.sort;
        default:
            return state;
    }
}

const rootReducer = combineReducers({items, columns, sort});

function itemsFetchDataSuccess(items) {
    return {
        type: 'ITEMS_FETCH_DATA_SUCCESS',
        items
    };
}

function itemsFetchData(url) {
    return (dispatch) => {
       setTimeout(() => {
           dispatch(itemsFetchDataSuccess(JSONSchemaFaker(schema)))
       }, 1000)
    };
}

function setSort(sort) {
    return {
        type: 'SET_SORT',
        sort
    }
}

function itemsSort(items, sort, col) {
    return dispatch => {
        const newItems = [...items];
        const sortDir = sort.by === col.name ? sortDirs[sort.dir] : sort.dir;
        const sortDirFactor = sortFactors[sortDir];
        newItems.sort((i1, i2) => {
            return i1[col.name] > i2[col.name] ? sortDirFactor : -sortDirFactor;
        })
        dispatch(itemsFetchDataSuccess(newItems));
        dispatch(setSort({by: col.name, dir: sortDir}));
    }
}