import React, { Component } from "react";
import { Tooltip, Layout, PageHeader, Popconfirm, Tag, List, InputNumber, Table, Modal, message, Progress, Badge, Descriptions, Tree, Row, Col, Card, Select, Typography, Upload, Button, Space, Input, Form, Radio, Divider, Collapse, Checkbox, Tabs, Steps } from 'antd';
import {
	InfoCircleOutlined,
	DeleteOutlined,
	LineOutlined,
	FireOutlined,
	ClockCircleOutlined,
	CheckCircleOutlined,
	FileDoneOutlined,
	FileOutlined,
	AimOutlined,
	ToolOutlined,
	ExportOutlined,
	ExperimentOutlined,
	ReloadOutlined,
	PushpinOutlined,
	PlayCircleOutlined,
	PauseOutlined,
	CaretRightOutlined,
	StepForwardOutlined,
	CloseOutlined,
	ControlOutlined,
	CodeOutlined,
	EnvironmentOutlined
} from '@ant-design/icons';

import EventBus from "./eventbus/EventBus";
import TasksStats from './stats/tasks';

import moment from "moment"

const { Content } = Layout;
const { Dragger } = Upload;
const { Option } = Select;
const { Text, Title, Paragraph } = Typography;
const { Panel } = Collapse;
const { TabPane } = Tabs;
const { Step } = Steps;
const { TreeNode } = Tree;

// https://github.com/hashcat/hashcat/blob/master/include/types.h
const HASHCAT_STATUS_INIT             = 0,
	HASHCAT_STATUS_AUTOTUNE           = 1,
	HASHCAT_STATUS_SELFTEST           = 2,
	HASHCAT_STATUS_RUNNING            = 3,
	HASHCAT_STATUS_PAUSED             = 4,
	HASHCAT_STATUS_EXHAUSTED          = 5,
	HASHCAT_STATUS_CRACKED            = 6,
	HASHCAT_STATUS_ABORTED            = 7,
	HASHCAT_STATUS_QUIT               = 8,
	HASHCAT_STATUS_BYPASS             = 9,
	HASHCAT_STATUS_ABORTED_CHECKPOINT = 10,
	HASHCAT_STATUS_ABORTED_RUNTIME    = 11,
	HASHCAT_STATUS_ERROR              = 13,
	HASHCAT_STATUS_ABORTED_FINISH     = 14,
	HASHCAT_STATUS_AUTODETECT         = 16;

const HASHCAT_STATUS_MESSAGES = {
	[HASHCAT_STATUS_INIT]               : "Init",
	[HASHCAT_STATUS_AUTOTUNE]           : "Autotune",
	[HASHCAT_STATUS_SELFTEST]           : "Selftest",
	[HASHCAT_STATUS_RUNNING]            : "Running",
	[HASHCAT_STATUS_PAUSED]             : "Paused",
	[HASHCAT_STATUS_EXHAUSTED]          : "Exhausted",
	[HASHCAT_STATUS_CRACKED]            : "Cracked",
	[HASHCAT_STATUS_ABORTED]            : "Aborted",
	[HASHCAT_STATUS_QUIT]               : "Quit",
	[HASHCAT_STATUS_BYPASS]             : "Bypass",
	[HASHCAT_STATUS_ABORTED_CHECKPOINT] : "Aborted (Checkpoint)",
	[HASHCAT_STATUS_ABORTED_RUNTIME]    : "Aborted (Runtime)",
	[HASHCAT_STATUS_ERROR]              : "Error",
	[HASHCAT_STATUS_ABORTED_FINISH]     : "Aborted (Finish)",
	[HASHCAT_STATUS_AUTODETECT]         : "Autodetect"
};

const HASHCAT_STATUS_BADGE_WARNING = [HASHCAT_STATUS_PAUSED];
const HASHCAT_STATUS_BADGE_PROCESSING = [HASHCAT_STATUS_RUNNING];
const HASHCAT_STATUS_BADGE_ERROR = [HASHCAT_STATUS_QUIT, HASHCAT_STATUS_ERROR];
const HASHCAT_STATUS_BADGE_SUCCESS = [HASHCAT_STATUS_CRACKED];
const HASHCAT_STATUS_BADGE_PINK = [HASHCAT_STATUS_EXHAUSTED];
const HASHCAT_STATUS_BADGE_YELLOW = [HASHCAT_STATUS_ABORTED, HASHCAT_STATUS_ABORTED_CHECKPOINT, HASHCAT_STATUS_ABORTED_RUNTIME, HASHCAT_STATUS_ABORTED_FINISH];

const PROCESS_STATUS_NOTSTARTED = 0,
	PROCESS_STATUS_RUNNING = 1,
	PROCESS_STATUS_FINISHED = 2;

function totalSpeed(devices) {
	var speed = 0;
	devices.forEach(device => {
		speed += device.speed;
	});
	return speed;
}

function humanizeSpeed(H) {
	let KH = 1000;
	let MH = KH * KH;
	let GH = MH * KH;
	let TH = GH * KH;

	if (H < KH*100) {
		return `${(H).toFixed(0)} H/s`
	} else if (H < MH) {
		return `${(H/KH).toFixed(1)} kH/s`
	} else if (H < GH) {
		return `${(H/MH).toFixed(1)} MH/s`
	} else if (H < TH) {
		return `${(H/GH).toFixed(1)} GH/s`
	} else {
		return `${(H/TH).toFixed(1)} TH/s`
	}
}

class Tasks extends Component {
	constructor(props) {
		super(props);

		this.onSelect = this.onSelect.bind(this);

		this.onClickStart = this.onClickStart.bind(this);
		this.onClickRefresh = this.onClickRefresh.bind(this);
		this.onClickPause = this.onClickPause.bind(this);
		this.onClickResume = this.onClickResume.bind(this);
		this.onClickCheckpoint = this.onClickCheckpoint.bind(this);
		this.onClickSkip = this.onClickSkip.bind(this);
		this.onClickQuit = this.onClickQuit.bind(this);

		this.onChangePriority = this.onChangePriority.bind(this);

		this.onClickArguments = this.onClickArguments.bind(this);
		this.onClickDelete = this.onClickDelete.bind(this);

		this.state = {
			data: [],

			taskKey: undefined,
			task: undefined,

			isLoadingStart: false,
			isLoadingRefresh: false,
			isLoadingPause: false,
			isLoadingResume: false,
			isLoadingCheckpoint: false,
			isLoadingSkip: false,
			isLoadingQuit: false,

			isReadOnlyPriority: false,
			isLoadingDelete: false
		};
	}

	refresh() {
		this.setState({
			task: TasksStats.tasks[this.state.taskKey]
		});
	}

	onSelect(keys) {
		const taskKey = keys.shift();
		this.setState({
			taskKey: taskKey,
			task: TasksStats.tasks[taskKey]
		})
	}

	onClickStart() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOstartTask !== "function") {
			message.error("GOstartTask is not a function");
			return;
		}

		this.setState({isLoadingStart: true}, () => {
			window.GOstartTask(task.id).then(
				response => {
					this.setState({isLoadingStart: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingStart: false});
				}
			);
		})
	}

	onClickRefresh() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOrefreshTask !== "function") {
			message.error("GOrefreshTask is not a function");
			return;
		}

		this.setState({isLoadingRefresh: true}, () => {
			window.GOrefreshTask(task.id).then(
				response => {
					this.setState({isLoadingRefresh: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingRefresh: false});
				}
			);
		})
	}

	onClickPause() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOpauseTask !== "function") {
			message.error("GOpauseTask is not a function");
			return;
		}

		this.setState({isLoadingPause: true}, () => {
			window.GOpauseTask(task.id).then(
				response => {
					this.setState({isLoadingPause: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingPause: false});
				}
			);
		})
	}

	onClickResume() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOresumeTask !== "function") {
			message.error("GOresumeTask is not a function");
			return;
		}

		this.setState({isLoadingResume: true}, () => {
			window.GOresumeTask(task.id).then(
				response => {
					this.setState({isLoadingResume: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingResume: false});
				}
			);
		})
	}

	onClickCheckpoint() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOcheckpointTask !== "function") {
			message.error("GOcheckpointTask is not a function");
			return;
		}

		this.setState({isLoadingCheckpoint: true}, () => {
			window.GOcheckpointTask(task.id).then(
				response => {
					this.setState({isLoadingCheckpoint: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingCheckpoint: false});
				}
			);
		})
	}

	onClickSkip() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOskipTask !== "function") {
			message.error("GOskipTask is not a function");
			return;
		}

		this.setState({isLoadingSkip: true}, () => {
			window.GOskipTask(task.id).then(
				response => {
					this.setState({isLoadingSkip: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingSkip: false});
				}
			);
		})
	}

	onClickQuit() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOquitTask !== "function") {
			message.error("GOquitTask is not a function");
			return;
		}

		this.setState({isLoadingQuit: true}, () => {
			window.GOquitTask(task.id).then(
				response => {
					this.setState({isLoadingQuit: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingQuit: false});
				}
			);
		})
	}

	onChangePriority(priority) {
		if (typeof(priority) !== "number")
			return

		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOpriorityTask !== "function") {
			message.error("GOpriorityTask is not a function");
			return;
		}

		this.setState({isReadOnlyPriority: true}, () => {
			window.GOpriorityTask(task.id, priority).then(
				response => {
					task.priority = priority;
					this.reBuildData();
					this.setState({isReadOnlyPriority: false});
					if (task.priority >= 0 && this.state.data[2].children.length === 0) {
						if (typeof window.GOstartNextTask === "function") {
							window.GOstartNextTask();
						}
					}
				},
				error => {
					message.error(error);
					this.setState({isReadOnlyPriority: false});
				}
			);
		})
	}

	onClickArguments() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		Modal.info({
			title: 'Arguments',
			content: (
				<div style={{ maxHeight: '300px', overflow: 'auto' }}>
					<Text code copyable>
						{task.arguments.join(" ")}
					</Text>
				</div>
			),
		});
	}

	onClickDelete() {
		const task = this.state.task;
		if (!task) {
			message.error("no task is selected");
			return;
		}

		if (typeof window.GOdeleteTask !== "function") {
			message.error("GOdeleteTask is not a function");
			return;
		}

		this.setState({isLoadingDelete: true}, () => {
			window.GOdeleteTask(task.id).then(
				response => {
					this.refresh();
					this.setState({isLoadingDelete: false});
				},
				error => {
					message.error(error);
					this.setState({isLoadingDelete: false});
				}
			);
		})
	}

	reBuildData() {
		var data = [
			{
				key: "Idle",
				title: "Idle",
				selectable: false,
				icon: <LineOutlined />,
				children: []
			},
			{
				key: "Queued",
				title: "Queued",
				selectable: false,
				icon: <ClockCircleOutlined />,
				children: []
			},
			{
				key: "In Progress",
				title: "In Progress",
				selectable: false,
				icon: <FireOutlined />,
				children: []
			},
			{
				key: "Finished",
				title: "Finished",
				selectable: false,
				icon: <CheckCircleOutlined />,
				children: []
			}
		];

		Object.values(TasksStats.tasks).forEach(task => {
			var category;
			switch (task.process.status) {
				case PROCESS_STATUS_NOTSTARTED:
					if (task.priority >= 0)
						category = data[1];
					else
						category = data[0];
					break;
				case PROCESS_STATUS_RUNNING:
					category = data[2];
					break;
				case PROCESS_STATUS_FINISHED:
					category = data[3];
					break;
				default:
					category = data[0];
					message.warning("unrecognized process status");
			}
			category.children.push({
				key: task.id,
				title: (
					task.stats.hasOwnProperty("progress") ? (
						task.id + " (" + Math.trunc((task.stats["progress"][0] / task.stats["progress"][1])*100) + "%)"
					) : (
						task.id
					)
				),
				icon: (
					task.stats.hasOwnProperty("status") ? (
						HASHCAT_STATUS_BADGE_WARNING.indexOf(task.stats["status"]) > -1 ? (
							<Badge status="warning" />
						) : HASHCAT_STATUS_BADGE_PROCESSING.indexOf(task.stats["status"]) > -1 ? (
							<Badge status="processing" />
						) : HASHCAT_STATUS_BADGE_ERROR.indexOf(task.stats["status"]) > -1 ? (
							<Badge status="error" />
						) : HASHCAT_STATUS_BADGE_SUCCESS.indexOf(task.stats["status"]) > -1 ? (
							<Badge status="success" />
						) : HASHCAT_STATUS_BADGE_PINK.indexOf(task.stats["status"]) > -1 ? (
							<Badge color="pink" />
						) : HASHCAT_STATUS_BADGE_YELLOW.indexOf(task.stats["status"]) > -1 ? (
							<Badge color="yellow" />
						) : (
							<Badge status="default" />
						)
					) : (
						<Badge color="#b7b7b7" />
					)
				),
			});
		});

		for (let i = 0; i < data.length; i++) {
			data[i].title = data[i].title + " (" + data[i].children.length + ")";
		}

		this.setState({
			data: data
		});
	}

	componentDidMount() {
		EventBus.on("tasksUpdate", "Tasks", () => {
			this.reBuildData();
		});

		this.reBuildData();
	}

	componentWillUnmount() {
		EventBus.remove("tasksUpdate", "Tasks");
	}

	render() {
		const { taskKey, task } = this.state;

		return (
			<>
				<PageHeader
					title="Tasks"
				/>
				<Content style={{ padding: '16px 24px' }}>
					<Row gutter={16} className="height-100 tree-height-100">
						<Col className="max-height-100" span={5}>
							<Tree
								showIcon
								blockNode
								treeData={this.state.data}
								onSelect={this.onSelect}
								selectedKeys={[taskKey]}
								style={{
									height: '100%',
									paddingRight: '.5rem',
									overflow: 'auto',
									background: '#0a0a0a',
									border: '1px solid #303030'
								}}
							/>
						</Col>
						<Col className="max-height-100" span={19}>
							{task ? (
								<Row gutter={[16, 14]} className="height-100" style={{ flexDirection: "column", flexWrap: "nowrap" }}>
									<Col flex="0 0 auto">
										<Row gutter={[16, 14]}>
											<Col span={24}>
												<PageHeader
													title={task.id}
													tags={
														task.stats.hasOwnProperty("status") ? (	
															HASHCAT_STATUS_BADGE_WARNING.indexOf(task.stats["status"]) > -1 ? (
																<Tag color="warning">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															) : HASHCAT_STATUS_BADGE_PROCESSING.indexOf(task.stats["status"]) > -1 ? (
																<Tag color="processing">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															) : HASHCAT_STATUS_BADGE_ERROR.indexOf(task.stats["status"]) > -1 ? (
																<Tag color="error">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															) : HASHCAT_STATUS_BADGE_SUCCESS.indexOf(task.stats["status"]) > -1 ? (
																<Tag color="success">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															) : HASHCAT_STATUS_BADGE_PINK.indexOf(task.stats["status"]) > -1 ? (
																<Tag color="pink">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															) : HASHCAT_STATUS_BADGE_YELLOW.indexOf(task.stats["status"]) > -1 ? (
																<Tag color="yellow">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															) : (
																<Tag color="default">{HASHCAT_STATUS_MESSAGES[task.stats["status"]]}</Tag>
															)
														) : null
													}
													style={{ padding: 0 }}
													extra={
														<Form layout="inline">
															<Form.Item
																label="Priority"
															>
																<InputNumber
																	min={-1}
																	max={999}
																	value={task.priority}
																	onChange={this.onChangePriority}
																	readOnly={this.state.isReadOnlyPriority}
																	bordered={false}
																/>
															</Form.Item>
															<Button
																icon={<ControlOutlined />}
																onClick={this.onClickArguments}
																style={{ marginRight: '1rem' }}
															>
																Arguments
															</Button>
															<Popconfirm
																placement="topRight"
																title="Are you sure you want to delete this task?"
																onConfirm={this.onClickDelete}
																okText="Yes"
																cancelText="No"
															>
																<Button
																	type="danger"
																	icon={<DeleteOutlined />}
																	loading={this.state.isLoadingDelete}
																>
																	Delete
																</Button>
															</Popconfirm>
														</Form>
													}
												/>
											</Col>
											<Col span={24}>
												{task.stats.hasOwnProperty("progress") ? (
													<Progress type="line" percent={Math.trunc((task.stats["progress"][0] / task.stats["progress"][1])*100)} />
												) : (
													<Progress type="line" percent={0} />
												)}
											</Col>
											<Col span={24}>
												<Row gutter={[12, 10]}>
													<Col>
														<Button
															type="primary"
															icon={<PlayCircleOutlined />}
															onClick={this.onClickStart}
															loading={this.state.isLoadingStart}
														>
															Start
														</Button>
													</Col>
													<Col>
														<Button
															icon={<ReloadOutlined />}
															onClick={this.onClickRefresh}
															loading={this.state.isLoadingRefresh}
														>
															Refresh
														</Button>
													</Col>
													<Col>
														<Button
															icon={<PauseOutlined />}
															onClick={this.onClickPause}
															loading={this.state.isLoadingPause}
														>
															Pause
														</Button>
													</Col>
													<Col>
														<Button
															icon={<CaretRightOutlined />}
															onClick={this.onClickResume}
															loading={this.state.isLoadingResume}
														>
															Resume
														</Button>
													</Col>
													<Col>
														<Button
															icon={<EnvironmentOutlined />}
															onClick={this.onClickCheckpoint}
															loading={this.state.isLoadingCheckpoint}
														>
															Checkpoint
														</Button>
													</Col>
													<Col>
														<Button
															icon={<StepForwardOutlined />}
															onClick={this.onClickSkip}
															loading={this.state.isLoadingSkip}
														>
															Skip
														</Button>
													</Col>
													<Col>
														<Popconfirm
															placement="topRight"
															title="Are you sure you want to quit this task?"
															onConfirm={this.onClickQuit}
															okText="Yes"
															cancelText="No"
														>
															<Button
																type="danger"
																icon={<CloseOutlined />}
																loading={this.state.isLoadingQuit}
															>
																Quit
															</Button>
														</Popconfirm>
													</Col>
												</Row>
											</Col>
										</Row>
									</Col>
									<Col flex="1 1 auto">
										<Row gutter={[16, 14]} className="height-100">
											<Col className="max-height-100" span={16}>
												<Descriptions
													column={2}
													layout="horizontal"
													bordered
												>
													{task.stats.hasOwnProperty("status") && (
														<Descriptions.Item label="Status" span={2}>
															{HASHCAT_STATUS_BADGE_WARNING.indexOf(task.stats["status"]) > -1 ? (
																<Badge status="warning" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															) : HASHCAT_STATUS_BADGE_PROCESSING.indexOf(task.stats["status"]) > -1 ? (
																<Badge status="processing" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															) : HASHCAT_STATUS_BADGE_ERROR.indexOf(task.stats["status"]) > -1 ? (
																<Badge status="error" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															) : HASHCAT_STATUS_BADGE_SUCCESS.indexOf(task.stats["status"]) > -1 ? (
																<Badge status="success" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															) : HASHCAT_STATUS_BADGE_PINK.indexOf(task.stats["status"]) > -1 ? (
																<Badge color="pink" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															) : HASHCAT_STATUS_BADGE_YELLOW.indexOf(task.stats["status"]) > -1 ? (
																<Badge color="yellow" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															) : (
																<Badge status="default" text={HASHCAT_STATUS_MESSAGES[task.stats["status"]]} />
															)}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("target") && (
														<Descriptions.Item label="Target" span={2}>
															{task.stats["target"]}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("progress") && (
														<Descriptions.Item label="Progress" span={2}>
															{task.stats["progress"][0] + " / " + task.stats["progress"][1] + " (" + Math.trunc((task.stats["progress"][0] / task.stats["progress"][1])*100) + "%)"}
															{task.stats.hasOwnProperty("guess") && (
																<Tooltip title={
																	<Descriptions bordered size="small" column={1} layout="horizontal">
																		{task.stats.guess.guess_base !== null ? (
																			<Descriptions.Item label="Guess Base">{task.stats.guess.guess_base} ({task.stats.guess.guess_base_offset}/{task.stats.guess.guess_base_count})</Descriptions.Item>
																		) : (
																			<Descriptions.Item label="Guess Base">-</Descriptions.Item>
																		)}
																		{task.stats.guess.guess_mod !== null ? (
																			<Descriptions.Item label="Guess Mod">{task.stats.guess.guess_mod} ({task.stats.guess.guess_mod_offset}/{task.stats.guess.guess_mod_count})</Descriptions.Item>
																		) : (
																			<Descriptions.Item label="Guess Mod">-</Descriptions.Item>
																		)}
																	</Descriptions>
																}>
																	<InfoCircleOutlined style={{ marginLeft: ".5rem" }} />
																</Tooltip>
															)}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("rejected") && (
														<Descriptions.Item label="Rejected" span={1}>
															{task.stats["rejected"]}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("restore_point") && (
														<Descriptions.Item label="Restore point" span={1}>
															{task.stats["restore_point"]}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("recovered_hashes") && (
														<Descriptions.Item label="Recovered hashes" span={1}>
															{task.stats["recovered_hashes"][0] + " / " + task.stats["recovered_hashes"][1] + " (" + Math.trunc((task.stats["recovered_hashes"][0] / task.stats["recovered_hashes"][1])*100) + "%)"}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("recovered_salts") && (
														<Descriptions.Item label="Recovered salts" span={1}>
															{task.stats["recovered_salts"][0] + " / " + task.stats["recovered_salts"][1] + " (" + Math.trunc((task.stats["recovered_salts"][0] / task.stats["recovered_salts"][1])*100) + "%)"}
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("devices") && (
														<Descriptions.Item label="Speed" span={2}>
															{humanizeSpeed(totalSpeed(task.stats["devices"]))}
															<Tooltip title={
																<Table
																	columns={[
																		{
																			title: 'ID',
																			dataIndex: 'id',
																			key: 'ID'
																		},
																		{
																			title: 'Speed',
																			dataIndex: 'speed',
																			key: 'Speed'
																		},
																		{
																			title: 'Temp',
																			dataIndex: 'temp',
																			key: 'Temp'
																		},
																		{
																			title: 'Util',
																			dataIndex: 'util',
																			key: 'Util'
																		}
																	]}
																	dataSource={task.stats["devices"].map(device =>
																		({
																			key: device.device_id,
																			id: device.device_id,
																			speed: humanizeSpeed(device.speed),
																			temp: device.hasOwnProperty("temp") ? device.temp + " °C": "-",
																			util: device.util + "%",
																		})
																	)}
																	size="small"
																	pagination={false}
																	style={{ overflow: 'auto' }}
																/>
															}>
																<InfoCircleOutlined style={{ marginLeft: ".5rem" }} />
															</Tooltip>
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("time_start") && (
														<Descriptions.Item label="Started" span={1}>
															<Tooltip title={moment.unix(task.stats["time_start"]).format("MMMM Do YYYY, HH:mm")}>
																{moment.unix(task.stats["time_start"]).fromNow()}
															</Tooltip>
														</Descriptions.Item>
													)}
													{task.stats.hasOwnProperty("estimated_stop") && (
														<Descriptions.Item label="ETA" span={1}>
															<Tooltip title={moment.unix(task.stats["estimated_stop"]).format("MMMM Do YYYY, HH:mm")}>
																{moment.unix(task.stats["estimated_stop"]).fromNow()}
															</Tooltip>
														</Descriptions.Item>
													)}
												</Descriptions>
											</Col>
											<Col className="max-height-100" span={8}>
												<div className="height-100" style={{ display: "flex", flexDirection: "column" }}>
												<span><CodeOutlined /> Terminal</span>
												<pre style={{
													flex: 'auto',
													overflow: 'auto',
													padding: '.5rem',
													margin: '0',
													border: '1px solid #303030'
												}}>
													{task.journal.map(j => j.message + "\n")}
												</pre>
												</div>
											</Col>
										</Row>
									</Col>
								</Row>
							) : (
								"No selected task."
							)}
						</Col>
					</Row>
				</Content>
			</>
		)
	}
}

export default Tasks;