const Desklet    = imports.ui.desklet;
const St         = imports.gi.St;
const GLib       = imports.gi.GLib;
const Mainloop   = imports.mainloop;
const Settings   = imports.ui.settings;
const ByteArray  = imports.byteArray;

function pad(n) {
    return String(n).padStart(2, '0');
}

function spawnRead(cmd) {
    try {
        let [, out] = GLib.spawn_command_line_sync(cmd);
        if (!out) return '';
        return ByteArray.toString(out).trim();
    } catch (e) {
        return '';
    }
}

function BirthDesklet(metadata, desklet_id) {
    this._init(metadata, desklet_id);
}

BirthDesklet.prototype = {
    __proto__: Desklet.Desklet.prototype,

    _init: function(metadata, desklet_id) {
        Desklet.Desklet.prototype._init.call(this, metadata, desklet_id);

        this.metadata       = metadata;
        this.desklet_id     = desklet_id;
        this._timeout       = null;
        this._birthTs       = 0;
        this._birthDateStr  = '';

        // defaults (overwritten by settings bind)
        this.date_font_size   = 14;
        this.age_font_size    = 13;
        this.padding          = 8;
        this.box_width        = 0;
        this.bg_opacity       = 70;

        this.settings = new Settings.DeskletSettings(this, this.metadata.uuid, this.desklet_id);
        this.settings.bindProperty(Settings.BindingDirection.IN,
            'date-font-size',      'date_font_size', this._applyStyles, null);
        this.settings.bindProperty(Settings.BindingDirection.IN,
            'age-font-size',       'age_font_size',  this._applyStyles, null);
        this.settings.bindProperty(Settings.BindingDirection.IN,
            'padding',             'padding',        this._applyStyles, null);
        this.settings.bindProperty(Settings.BindingDirection.IN,
            'box-width',           'box_width',      this._applyStyles, null);
        this.settings.bindProperty(Settings.BindingDirection.IN,
            'background-opacity',  'bg_opacity',     this._applyStyles, null);

        this._setupUI();
        this._detectBirth();
        this._tick();
    },

    _setupUI: function() {
        this._box   = new St.BoxLayout({ vertical: true });
        this._line1 = new St.Label({ text: '' });
        this._line2 = new St.Label({ text: '' });
        this._line1.clutter_text.set_use_markup(true);
        this._line2.clutter_text.set_use_markup(true);
        this._box.add(this._line1, { expand: true, x_fill: true });
        this._box.add(this._line2, { expand: true, x_fill: true });
        this.setContent(this._box);
        this.setHeader(_("System Birth"));
        this._setLine1('detecting…');
        this._applyStyles();
    },

    _applyStyles: function() {
        if (!this._box) return;
        let alpha  = (this.bg_opacity / 100).toFixed(2);
        let bg     = 'background-color: rgba(20, 20, 20, ' + alpha + ');';
        let border = 'border-color: rgba(255, 255, 255, ' + (this.bg_opacity / 100 * 0.15).toFixed(2) + ');';
        let w      = this.box_width > 0 ? 'min-width: ' + this.box_width + 'px;' : '';
        this.actor.style   = bg;
        this.content.style = bg + ' ' + border;
        this._header.style = bg;
        this._box.style    = 'padding: ' + this.padding + 'px ' + (this.padding + 4) + 'px; ' + w;
        this._line1.style = 'font-size: ' + this.date_font_size + 'px; text-align: center;';
        this._line2.style = 'font-size: ' + this.age_font_size  + 'px; text-align: center; margin-top: 4px; font-family: monospace;';
        // refresh markup so font sizes re-render
        if (this._birthDateStr) this._setLine1(this._birthDateStr);
    },

    _setLine1: function(dateStr) {
        this._birthDateStr = dateStr;
        this._line1.clutter_text.set_markup(
            '<span foreground="#ffcc44"><b>Birth: </b></span>' +
            '<span foreground="#ffffff">' + dateStr + '</span>'
        );
        this._line1.clutter_text.set_line_alignment(1); // 1 = Pango.Alignment.CENTER
    },

    _setLine2: function(ageStr) {
        this._line2.clutter_text.set_markup(
            '<span foreground="#44ddaa"><b>Age: </b></span>' +
            '<span foreground="#88ccff">' + ageStr + '</span>'
        );
        this._line2.clutter_text.set_line_alignment(1);
    },

    _detectBirth: function() {
        let d = null;

        // Arch / Manjaro: first line of pacman.log
        if (!d) {
            let line = spawnRead("bash -c \"head -1 /var/log/pacman.log 2>/dev/null\"");
            let m = line.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})/);
            if (m) d = new Date(m[1]);
        }

        // Debian / Ubuntu / Mint: oldest dpkg.log entry
        if (!d) {
            let line = spawnRead("bash -c \"ls -t /var/log/dpkg.log* 2>/dev/null | tail -1 | xargs zcat -f 2>/dev/null | head -1\"");
            let m = line.match(/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/);
            if (m) d = new Date(m[1]);
        }

        // Generic fallback: / filesystem birth timestamp (ext4)
        if (!d) {
            let line = spawnRead("bash -c \"stat / 2>/dev/null | awk '/Birth/{print $2, $3}'\"");
            let m = line.match(/(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})/);
            if (m) d = new Date(m[1] + 'T' + m[2]);
        }

        if (d && !isNaN(d.getTime())) {
            this._birthTs = d.getTime();
            this._setLine1(this._fmtDate(d));
        } else {
            this._setLine1('unknown');
        }
    },

    _fmtDate: function(d) {
        let mo = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
        return mo[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear() +
               '  ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
    },

    _calcAge: function() {
        if (!this._birthTs) return null;
        let b = new Date(this._birthTs);
        let n = new Date();

        let Y = n.getFullYear() - b.getFullYear();
        let M = n.getMonth()    - b.getMonth();
        let D = n.getDate()     - b.getDate();
        let h = n.getHours()    - b.getHours();
        let m = n.getMinutes()  - b.getMinutes();
        let s = n.getSeconds()  - b.getSeconds();

        if (s < 0) { s += 60; m--; }
        if (m < 0) { m += 60; h--; }
        if (h < 0) { h += 24; D--; }
        if (D < 0) { D += new Date(n.getFullYear(), n.getMonth(), 0).getDate(); M--; }
        if (M < 0) { M += 12; Y--; }

        return Y + 'y  ' + M + 'mo  ' + D + 'd   ' + pad(h) + ':' + pad(m) + ':' + pad(s);
    },

    _tick: function() {
        let age = this._calcAge();
        if (age) this._setLine2(age);
        this._timeout = Mainloop.timeout_add_seconds(1, () => {
            this._tick();
            return false;
        });
    },

    on_desklet_removed: function() {
        if (this._timeout) {
            Mainloop.source_remove(this._timeout);
            this._timeout = null;
        }
    }
};

function main(metadata, desklet_id) {
    return new BirthDesklet(metadata, desklet_id);
}
