/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

import cockpit from "cockpit";
import React from "react";
import { get_active_usage, teardown_active_usage, fmt_size, decode_filename } from "./utils.js";
import dialog from "./dialog.js";
import { StdDetailsLayout } from "./details.jsx";
import Content from "./content-views.jsx";
import { StorageButton, StorageOnOff, StorageMultiAction, StorageBlockNavLink } from "./storage-controls.jsx";

import inotify_py from "raw!inotify.py";
import vdo_monitor_py from "raw!./vdo-monitor.py";

const _ = cockpit.gettext;

function wait_for(client, cond) {
    var dfd = cockpit.defer();

    function check() {
        var res = cond();
        if (res) {
            client.removeEventListener("changed", check);
            dfd.resolve(res);
        }
    }

    client.addEventListener("changed", check);
    check();

    return dfd.promise();
}

export class VDODetails extends React.Component {
    constructor() {
        super();
        this.poll_path = null;
        this.state = { stats: null };
    }

    ensure_polling(path) {
        var buf = "";

        if (this.poll_path === path)
            return;

        if (this.poll_path) {
            this.poll_process.close();
            this.setState({ stats: null });
        }

        if (path)
            this.poll_process = cockpit.spawn([ "python", "--", "-", path ], { superuser: true })
                                       .input(inotify_py + vdo_monitor_py)
                                       .stream((data) => {
                                           buf += data;
                                           var lines = buf.split("\n");
                                           buf = lines[lines.length-1];
                                           if (lines.length >= 2) {
                                               this.setState({ stats: JSON.parse(lines[lines.length-2]) });
                                           }
                                       });
        this.poll_path = path;
    }

    componentWillUnmount() {
        this.ensure_polling(null);
    }

    render() {
        var client = this.props.client;
        var vdo = this.props.vdo;
        var block = client.slashdevs_block[vdo.dev];
        var backing_block = client.slashdevs_block[vdo.backing_dev];

        function force_delete() {
            var location = cockpit.location;
            return vdo.force_remove().then(function () {
                location.go("/");
            });
        }

        if (vdo.broken) {
            var broken = (
                <div className="alert alert-danger">
                    <div className="pull-right">
                        <StorageButton onClick={force_delete}>{_("Remove device")}</StorageButton>
                    </div>
                    <span className="pficon pficon-error-circle-o"></span>
                    <strong>{_("The creation of this VDO device did not finish and the device can't be used.")}</strong>
                </div>
            );
            return <StdDetailsLayout client={this.props.client} alert={broken}/>;
        }

        this.ensure_polling(block ? vdo.dev : null);

        var alert = null;
        if (backing_block && backing_block.Size > vdo.physical_size)
            alert = (
                <div className="alert alert-warning">
                    <div className="pull-right">
                        <StorageButton onClick={vdo.grow_physical}>{_("Grow to take all space")}</StorageButton>
                    </div>
                    <span className="pficon pficon-warning-triangle-o"></span>
                    <strong>{_("This VDO device does not use all of its backing device.")}</strong>  <span>
                        { cockpit.format(_("Only $0 of $1 are used."),
                                         fmt_size(vdo.physical_size),
                                         fmt_size(backing_block.Size))
                        }
                    </span>
                </div>
            );

        function stop() {
            var usage = get_active_usage(client, block? block.path : "/");

            if (usage.Blocking) {
                dialog.open({ Title: cockpit.format(_("$0 is in active use"), vdo.name),
                              Fields: [ ],
                              Blocking: usage.Blocking,
                });
                return;
            }

            if (usage.Teardown) {
                dialog.open({ Title: cockpit.format(_("Please confirm stopping of $0"),
                                                    vdo.name),
                              Teardown: usage.Teardown,
                              Fields: [ ],
                              Action: {
                                  Title: _("Stop"),
                                  action: function () {
                                      return teardown_active_usage(client, usage)
                                          .then(function () {
                                              return vdo.stop();
                                          });
                                  }
                              }
                });
            } else {
                return vdo.stop();
            }
        }

        function delete_() {
            var usage = get_active_usage(client, block? block.path : "/");

            if (usage.Blocking) {
                dialog.open({ Title: cockpit.format(_("$0 is in active use"), vdo.name),
                              Fields: [ ],
                              Blocking: usage.Blocking,
                });
                return;
            }

            function teardown_configs() {
                if (block) {
                    return block.Format("empty", { 'tear-down': { t: 'b', v: true } });
                } else {
                    return vdo.start()
                              .then(function () {
                                  wait_for(client, () => client.slashdevs_block[vdo.dev])
                                    .then(function (block) {
                                        return block.Format("empty", { 'tear-down': { t: 'b', v: true } });
                                    });
                              })
                }
            }

            dialog.open({ Title: cockpit.format(_("Please confirm deletion of $0"),
                                                vdo.name),
                          Teardown: usage.Teardown,
                          Fields: [ ],
                          Action: {
                              Title: _("Delete"),
                              Danger: _("Deleting a VDO device will erase all data on it."),
                              action: function () {
                                  return teardown_active_usage(client, usage)
                                      .then(function () {
                                          return teardown_configs()
                                              .then(function () {
                                                  var location = cockpit.location;
                                                  return vdo.remove().then(function () {
                                                      location.go("/");
                                                  });
                                              });
                                      });
                              }
                          }
            });
        }

        function grow_logical() {
            dialog.open({ Title: cockpit.format(_("Grow logical size of $0"), vdo.name),
                          Fields: [
                              { SizeSlider: "lsize",
                                Title: _("Logical Size"),
                                Max: 5*vdo.logical_size,
                                Min: vdo.logical_size,
                                Round: 512,
                                Value: vdo.logical_size,
                                AllowInfinite: true
                              },
                          ],
                          Action: {
                              Title: _("Grow"),
                              action: function (vals) {
                                  if (vals.lsize > vdo.logical_size)
                                      return vdo.grow_logical(vals.lsize).then(() => {
                                          if (block && block.IdUsage == "filesystem")
                                              return cockpit.spawn([ "fsadm", "resize",
                                                                     decode_filename(block.Device) ],
                                                                   { superuser: true });
                                      });
                              }
                          }
            });
        }

        var actions = [
            { title: _("Start"), action: vdo.start },
            { title: _("Stop"), action: stop },
            { title: _("Delete"), action: delete_ }
        ];

        function fmt_perc(num) {
            if (num || num == 0)
                return num + "%";
            else
                return "--";
        }

        var stats = this.state.stats;

        var header = (
            <div className="panel panel-default">
                <div className="panel-heading">
                    {cockpit.format(_("VDO Device $0"), vdo.name)}
                    <span className="pull-right">
                        <StorageMultiAction actions={actions} default={block? 1 : 0}/>
                    </span>
                </div>
                <div className="panel-body">
                    <table className="info-table-ct">
                        <tr>
                            <td>{_("Device File")}</td>
                            <td>{vdo.dev}</td>
                        </tr>
                        <tr>
                            <td>{_("Backing Device")}</td>
                            <td>
                                { backing_block ?
                                  <StorageBlockNavLink client={client} block={backing_block}/>
                                  : vdo.backing_dev
                                }
                            </td>
                        </tr>
                        <tr>
                            <td>{_("Physical")}</td>
                            <td>
                                { stats ?
                                  cockpit.format(_("$0 data + $1 overhead used of $2 ($3)"),
                                                 fmt_size(stats.dataBlocksUsed * stats.blockSize),
                                                 fmt_size(stats.overheadBlocksUsed * stats.blockSize),
                                                 fmt_size(vdo.physical_size),
                                                 fmt_perc(stats.usedPercent))
                                      : fmt_size(vdo.physical_size)
                                }
                            </td>
                        </tr>
                        <tr>
                            <td>{_("Logical")}</td>
                            <td>
                                { stats ?
                                  cockpit.format(_("$0 used of $1 ($2 saved)"),
                                                 fmt_size(stats.logicalBlocksUsed * stats.blockSize),
                                                 fmt_size(vdo.logical_size),
                                                 fmt_perc(stats.savingPercent))
                                      : fmt_size(vdo.logical_size)
                                }
                                &nbsp; <StorageButton onClick={grow_logical}>{_("Grow")}</StorageButton>
                            </td>
                        </tr>
                        <tr>
                            <td>{_("Index Memory")}</td>
                            <td>{fmt_size(vdo.index_mem * 1024*1024*1024)}</td>
                        </tr>
                        <tr>
                            <td>{_("Compression")}</td>
                            <td><StorageOnOff state={vdo.compression}
                                              onChange={() => vdo.set_compression(!vdo.compression)}/>
                            </td>
                        </tr>
                        <tr>
                            <td>{_("Deduplication")}</td>
                            <td><StorageOnOff state={vdo.deduplication}
                                              onChange={() => vdo.set_deduplication(!vdo.deduplication)}/>
                            </td>
                        </tr>
                    </table>
                </div>
            </div>
        );

        var content = <Content.Block client={client} block={block} allow_partitions={false}/>;

        return <StdDetailsLayout client={this.props.client}
                                 alert={alert}
                                 header={header}
                                 content={content}/>;
    }
}
