(function(back) {
  // define settings filename
  var filename = "sleeplog.json";
  // define logging prompt display status
  var movementThresholdsPrompt = true;
  var hrmThresholdsPrompt = true;

  // define default vaules
  var defaults = {
    // main settings
    enabled: true, //   en-/disable completely
    // threshold settings
    maxAwake: 36E5, //  [ms] maximal awake time to count for consecutive sleep
    minConsec: 18E5, // [ms] minimal time to count for consecutive sleep
    deepTh: 150, //     threshold for deep sleep
    lightTh: 300,//    threshold for light sleep
    hrmLightTh: 74,//    threshold for light sleep with HRM
    hrmDeepTh:60,//     threshold for deep sleep with HRM
    sleepMode: 0, // 0=Movement, 1=HRM, 2=Both
    wearTemp: 19.5, //    temperature threshold to count as worn
    // app settings
    breakToD: 12, //    [h] time of day when to start/end graphs
    appTimeout: 0 //   lock and backlight timeouts for the app
  };

  // assign loaded settings to default values
  var settings = Object.assign({},defaults, require("Storage").readJSON(filename, true));

  // --- MIGRATION LOGIC (Added in v0.26) ---
  // WHY: In v0.25 and earlier, the HRM setting was a simple boolean called 'preferHRM'.
  // In v0.26, this was replaced by a 3-state 'sleepMode' (0=Movement, 1=HRM, 2=Both).
  // WHAT: This block silently migrates existing users to the new format upon opening
  // the settings, ensuring they don't lose their preference and the app doesn't crash.
  // REMOVAL: This block can be safely removed in a future major update (e.g., v1.0 or 
  // after ~1 year), once we can assume all active users have updated past v0.25.
  // CONSEQUENCE: If removed, users updating directly from <=v0.25 to that future version 
  // will simply lose their old 'preferHRM' preference and default to sleepMode 0.
  if ("preferHRM" in settings) {
    settings.sleepMode = settings.preferHRM ? 1 : 0;
    delete settings.preferHRM;
    require("Storage").writeJSON(filename, settings);
  }
  // ----------------------------------------

  // write change to storage
  function writeSetting() {
    require("Storage").writeJSON(filename, settings);
  }

  // plot a debug file
  function plotDebug(filename) {    
    // handle swipe events
    function swipeHandler(x, y) {
      if (x) {
        start -= x;
        if (start < 0 || maxStart && start > maxStart) {
          start = start < 0 ? 0 : maxStart;
        } else {
          drawGraph();
        }
      } else {
        minMove += y * 10;
        if (minMove < 0 || minMove > 300) {
          minMove = minMove < 0 ? 0 : 300;
        } else {
          drawGraph();
        }
      }
    }
    // handle touch events
    function touchHandler() {
      invert = !invert;
      drawGraph();
    }

    // read required entries
    function readEntries(count) {
      // extract usabble data from line
      function extract(line) {
        if (!line) return;
        line = line.trim().split(",");
        return [Math.round((parseFloat(line[0]) - 25569) * 144), parseInt(line[1])];
      }

      // open debug file
      var file = require("Storage").open(filename, "r");
      // skip title
      file.readLine();
      // skip past entries
      for (var i = 0; i < start * count; i++) { file.readLine(); }
      // define data with first entry
      var data = [extract(file.readLine())];
      // get start time in 10min steps
      var start10min = data[0][0];
      // read first required entry
      var line = extract(file.readLine());
      
      // read next count entries from file
      while (data.length < count) {
        // check if line is immediately after the last entry
        if (line[0] === start10min + data.length) {
          // add line to data
          data.push(line);
          // read new line
          line = extract(file.readLine());
          // stop if no more data available
          if (!line) break;
        } else {
          // add line with unknown movement
          data.push([start10min + data.length, 0]);
        }
      }

      // free ram
      file = undefined
      // set this start as max, if less entries than expected
      if (data.length < count) maxStart = start;
      return data;
    }

    // draw graph at starting point
    function drawGraph() {
      // set correct or inverted drawing
      function rect(fill, x0, y0, x1, y1) {
        if (fill ^ invert) {
          g.fillRect(x0, y0, x1, y1);
        } else {
          g.clearRect(x0, y0, x1, y1);
        }
      }

      // set witdh
      var width = g.getWidth();
      // calculate entries to display (+ set width zero based)
      var count = (width--) / 4;
      // read required entries
      var data = readEntries(count);

      // clear app area
      g.reset().clearRect(0, width - 13, width, width);
      rect(false, 0, 24, width, width - 14);
      // draw x axis
      g.drawLine(0, width - 13, width, width - 13);
      // draw x label
      data.forEach((e, i) => {
        var startTime = new Date(e[0] * 6E5);
        if (startTime.getMinutes() === 0) {
          g.fillRect(4 * i, width - 12, 4 * i, width - 9);
          g.setFontAlign(-1, -1).setFont("6x8")
            .drawString(startTime.getHours(), 4 * i + 1, width - 8);
        } else if (startTime.getMinutes() === 30) {
          g.fillRect(4 * i, width - 12, 4 * i, width - 11);
        }
      });

      // calculate max height
      var height = width - 38;
      // cycle through entries
      data.forEach((e, i) => {
        // check if movement available 
        if (e[1]) {
          // set color depending on recognised status
          var color = e[1] < deepTh ? 31 : e[1] < lightTh ? 2047 : 2016;
          // correct according to min movement
          e[1] -= minMove;
          // keep movement in bounderies
          e[1] = e[1] < 0 ? 0 : e[1] > height ? height : e[1];
          // draw line and rectangle
          g.reset();
          rect(true, 4 * i, width - 14, 4 * i, width - 14 - e[1]);
          g.setColor(color).fillRect(4 * i + 1, width - 14, 4 * i + 3, width - 14 - e[1]);
        } else {
          // draw error in red
          g.setColor(63488).fillRect(4 * i, width - 14, 4 * i, width - 14 - height);
        }
      });
      // draw threshold lines
      [deepTh, lightTh].forEach(th => {
        th -= minMove;
        if (th > 0 && th < height) {
          // draw line
          g.reset();
          rect(true, 0, width - 14 - th, width, width - 14 - th);
          // draw value above or below line
          var yAlign = th < height / 2 ? -1 : 1;
          if (invert) g.setColor(1);
          g.setFontAlign(1, yAlign).setFont("6x8")
            .drawString(th + minMove, width - 2, width - 13 - th + 10 * yAlign);
        }
      });

      // free ram
      data = undefined;
    }

    // get thresholds
    var deepTh = global.sleeplog ? global.sleeplog.conf.deepTh : defaults.deepTh;
    var lightTh = global.sleeplog ? global.sleeplog.conf.lightTh : defaults.lightTh;
    // set lowest movement displayed
    var minMove = deepTh - 20;
    // set start point
    var start = 0;
    // define max start point value
    var maxStart = 0;
    // define inverted color status
    var invert = false;

    // setup UI
    Bangle.setUI({
      mode: "custom",
      back: selectDebug,
      touch: touchHandler,
      swipe: swipeHandler
    });

    // first draw
    drawGraph(start);
  }

  // select a debug logfile
  function selectDebug() {
    // load debug files
    var files = require("Storage").list(/^sleeplog_\d\d\d\d\d\d\.csv$/, {sf:true});

    // check if no files found
    if (!files.length) {
      // show prompt
      E.showPrompt( /*LANG*/"No debug files found.", {
        title: /*LANG*/"Debug log",
        buttons: {
          /*LANG*/"Back": 0
        }
      }).then(showDebug);
    } else {
      // prepare scroller
      const H = 40;
      var menuIcon = "\0\f\f\x81\0\xFF\xFF\xFF\0\0\0\0\x0F\xFF\xFF\xF0\0\0\0\0\xFF\xFF\xFF";
      // show scroller
      E.showScroller({
        h: H, c: files.length,
        back: showDebug,
        scrollMin : -24, scroll : -24, // title is 24px, rendered at -1
          draw : (idx, r) => {
            if (idx < 0) {
              return g.setFont("12x20").setFontAlign(-1,0).drawString(menuIcon + " Select file", r.x + 12, r.y + H - 12);
            } else {
              g.setColor(g.theme.bg2).fillRect({x: r.x + 4, y: r.y + 2, w: r.w - 8, h: r.h - 4, r: 5});
              var name = new Date(parseInt(files[idx].match(/\d\d\d\d\d\d/)[0]) * 36E5);
              name = name.toString().slice(0, -12).split(" ").filter((e, i) => i !== 3).join(" ");
              g.setColor(g.theme.fg2).setFont("12x20").setFontAlign(-1, 0).drawString(name, r.x + 12, r.y + H / 2);
            }
          },
        select: (idx) => plotDebug(files[idx])
      });
    }
  }

  // show menu or promt to change debugging
  function showDebug() {
    // check if sleeplog is available
    if (global.sleeplog) {
      // get debug status, file and duration
      var enabled = !!global.sleeplog.debug;
      var file = typeof global.sleeplog.debug === "object";
      var duration = 0;
      // setup debugging menu
      var debugMenu = {
        "": {
          title: /*LANG*/"Debugging",
          back: () => {
            // check if some value has changed
            if (enabled !== !!global.sleeplog.debug || file !== (typeof global.sleeplog.debug === "object") || duration)
              require("sleeplog").setDebug(enabled, file ? duration || 12 : undefined);
            // redraw main menu
            showMain(7);
          }
        },
        /*LANG*/"View log": () => selectDebug(),
        /*LANG*/"Enable": {
          value: enabled,
          onchange: v => enabled = v
        },
        /*LANG*/"write File": {
          value: file,
          onchange: v => file = v
        },
        /*LANG*/"Duration": {
          value: file ? (global.sleeplog.debug.writeUntil - Date.now()) / 36E5 | 0 : 12,
          min: 1,
          max: 96,
          wrap: true,
          format: v => v + /*LANG*/ "h",
          onchange: v => duration = v
        },
        /*LANG*/"Cancel": () => showMain(7),
      };
      // show menu
      /*var menu =*/ E.showMenu(debugMenu);
    } else {
      // show error prompt
      E.showPrompt("Sleeplog" + /*LANG*/"not enabled!", {
        title: /*LANG*/"Debugging",
        buttons: {
          /*LANG*/"Back": 7
        }
      }).then(showMain);
    }
  }
  
  
  // show menu to change thresholds
  function showHRMThresholds() {
    // setup logging menu
    //var menu;
    var thresholdsMenu = {
      "": {
        title: /*LANG*/"HRM Thresholds",
        back: () => showMain(2)
      },
      /*LANG*/"Deep Sleep": {
        value: settings.hrmDeepTh,
        step: 1,
        min: 30,
        max: 200,
        wrap: true,
        noList: true,
        onchange: v => {
          settings.hrmDeepTh = v;
          writeSetting();
        }
      },
      /*LANG*/"Light Sleep": {
        value: settings.hrmLightTh,
        step: 1,
        min: 30,
        max: 200,
        wrap: true,
        noList: true,
        onchange: v => {
          settings.hrmLightTh = v;
          writeSetting();
        }
      },
      /*LANG*/"Reset to Default": () => {
        settings.hrmDeepTh = defaults.hrmDeepTh;
        settings.hrmLightTh = defaults.hrmLightTh;
        writeSetting();
        showHRMThresholds();
      }
    };
    if (hrmThresholdsPrompt) {
      hrmThresholdsPrompt = false;
      E.showPrompt("SleepLog only uses HRM if HRM is enabled in Health settings.", {
        title: /*LANG*/"HRM Thresholds",
        buttons: {
          /*LANG*/"Ok": 0
        }
      }).then(() => /*menu =*/ E.showMenu(thresholdsMenu));
    } else {
      /*menu =*/ E.showMenu(thresholdsMenu);
    }
  }

  // show menu to change thresholds
  function showMovementThresholds() {
    // setup logging menu
    //var menu;
    var thresholdsMenu = {
      "": {
        title: /*LANG*/"Movement Thresholds",
        back: () => showMain(2)
      },
      
      /*LANG*/"Deep Sleep": {
        value: settings.deepTh,
        step: 1,
        min: 30,
        max: 500,
        wrap: true,
        noList: true,
        onchange: v => {
          settings.deepTh = v;
          writeSetting();
        }
      },
      /*LANG*/"Light Sleep": {
        value: settings.lightTh,
        step: 1,
        min: 100,
        max: 800,
        wrap: true,
        noList: true,
        onchange: v => {
          settings.lightTh = v;
          writeSetting();
        }
      },
      
      /*LANG*/"Reset to Default": () => {
        settings.maxAwake = defaults.maxAwake;
        settings.minConsec = defaults.minConsec;
        settings.deepTh = defaults.deepTh;
        settings.lightTh = defaults.lightTh;
        writeSetting();
        showMovementThresholds();
      }
    }
    if (movementThresholdsPrompt) {
      movementThresholdsPrompt = false;
      E.showPrompt("Changes take effect from now on, not retrospective", {
        title: /*LANG*/"Movement Thresholds",
        buttons: {
          /*LANG*/"Ok": 0
        }
      }).then(() => /*menu =*/ E.showMenu(thresholdsMenu));
    } else {
      /*menu =*/ E.showMenu(thresholdsMenu);
    }
    }
    
    function showOtherSettings() {
    // setup logging menu
    //var menu;
      var otherSettingsMenu = {
        "": {
          title: /*LANG*/"Other Settings",
          back: () => showMain(2)
        },
        /*LANG*/"Max Awake": {
          value: settings.maxAwake / 6E4,
          step: 10,
          min: 10,
          max: 120,
          wrap: true,
          noList: true,
          format: v => v + /*LANG*/"min",
          onchange: v => {
            settings.maxAwake = v * 6E4;
            writeSetting();
          }
        },
        /*LANG*/"Min Consecutive": {
          value: settings.minConsec / 6E4,
          step: 10,
          min: 10,
          max: 120,
          wrap: true,
          noList: true,
          format: v => v + /*LANG*/"min",
          onchange: v => {
            settings.minConsec = v * 6E4;
            writeSetting();
          }
        },
        /*LANG*/"Sleep Mode": {
          value: settings.sleepMode,
          min: 0,
          max: 2,
          format: v => [/*LANG*/"Movement", /*LANG*/"HRM", /*LANG*/"Both"][v],
          onchange: v => {
            settings.sleepMode = v;
            writeSetting();
          }
        },
        /*LANG*/"HRM Thresholds": () => showHRMThresholds(),
        /*LANG*/"Wear Temp": {
          value: settings.wearTemp,
          step: 0.5,
          min: 19.5,
          max: 40,
          wrap: true,
          noList: true,
          format: v => v === 19.5 ? "Disabled" : require("locale").temp(v),
          onchange: v => {
            settings.wearTemp = v;
            writeSetting();
          }
        },
        /*LANG*/"Break ToD": {
        value: settings.breakToD,
        step: 1,
        min: 0,
        max: 23,
        wrap: true,
        noList: true,
        format: v => v + ":00",
        onchange: v => {
          settings.breakToD = v;
          writeSetting();
        }
      },
      /*LANG*/"App Timeout": {
        value: settings.appTimeout / 1E3,
        step: 10,
        min: 0,
        max: 120,
        wrap: true,
        noList: true,
        format: v => v ? v + "s" : "-",
        onchange: v => {
          settings.appTimeout = v * 1E3;
          writeSetting();
        }
      },
        /*LANG*/"Reset to Default": () => {
          settings.maxAwake = defaults.maxAwake;
          settings.minConsec = defaults.minConsec;
          settings.breakToD = defaults.breakToD;
          settings.appTimeout = defaults.appTimeout;

          writeSetting();
          showOtherSettings();
        }
      };
      E.showMenu(otherSettingsMenu);
    }

    

  // show main menu
  function showMain(selected) {
    // set debug image
    var debugImg = !global.sleeplog ?
      "FBSBAOAAfwAP+AH3wD4+B8Hw+A+fAH/gA/wAH4AB+AA/wAf+APnwHw+D4Hx8A++AH/AA/gAH" : // X
      typeof global.sleeplog.debug === "object" ?
      "FBSBAB/4AQDAF+4BfvAX74F+CBf+gX/oFJKBf+gUkoF/6BSSgX/oFJ6Bf+gX/oF/6BAAgf/4" : // file
      global.sleeplog.debug ?
      "FBSBAP//+f/V///4AAGAABkAAZgAGcABjgAYcAGDgBhwAY4AGcABmH+ZB/mAABgAAYAAH///" : // console
      0; // off
    debugImg = debugImg ? "\0" + atob(debugImg) : false;
    // set menu
    var mainMenu = {
      "": {
        title: "Sleep Log",
        back: back,
        selected: selected
      },
      /*LANG*/"Movement Thresholds": () => showMovementThresholds(),
      /*LANG*/"Other Settings": () => showOtherSettings(),
      
      /*LANG*/"Enabled": {
        value: settings.enabled,
        onchange: v => {
          settings.enabled = v;
          require("sleeplog").setEnabled(v);
        }
      },
      /*LANG*/"Debugging": {
        value: debugImg,
        onchange: () => setTimeout(showDebug, 10)
      }
    };
    /*var menu =*/ E.showMenu(mainMenu);
  }

  // draw main menu
  showMain();
})
