// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: broadcast-tower;

/**************
Version 2.0.0

Changelog:  
  v2.0.0:
          - Disable Dark Modus Support
          - Medium & large Widget Support
          - switch between used and remaining volume
          - use MSISDN for Cache-Name
          - show amount for prepaid cards
          - show remaining days as progress-bar
  v1.2.4:
          - use color.dynamic
  v1.2.3:
          - Fix typo (thanks @CuzImStantac)
  v1.2.2:
          - Fix remaining Day issue
  v1.2.1:
          - Code '40100' added for CallYa Tariff
  v1.2.0:
          - Option to set colors by widget parameters removed
          - Option to set MeinVodafone credentails via widget parameters added
  v1.1.1:
          - new CallYa Tariff added: C_DIY_Data_National
  v1.1.0:
          - Login via MeinVodafone Login added
          - Show Remaining Days
          - MegaByte Support added for tariffs with <= 1 GB 
  v1.0.3:
          - CallYa Support
          - Write more useful information in the log, so that you can add support yourself if necessary
  v1.0.2:
          - Enhanced logging for CallYa troubeshooting
  v1.0.1:
          - Better Error handling
          - Better logging
          - Fallback Widget screen in case of an error

If you have problems or need help, ask for support here: https://github.com/ThisIsBenny/iOS-Widgets/issues

Credits: 
  - Sillium@GitHub (https://gist.github.com/Sillium/f904fb89444bc8dde12cfc07b8fa8728)
  - Chaeimg@Github (https://github.com/chaeimg/battCircle)
**************/

////////////////////////////////////////////////////////////////////////////////
//////////////////////////         User-Config         /////////////////////////
////////////////////////////////////////////////////////////////////////////////

// How many minutes should the cache be valid
const cacheMinutes = 60

// Set to false if the widget should display always in red
let darkModeSupport = true

// Switch between remaining and used contingent. If you want show the used contingent, then change the value from true to false
let showRemainingContingent = true

// To disable the progressbar for the remaining days, you have to change the value from true to false
let showRemainingDaysAsProgressbar = true

// Please add additional values to these list, in case that your contract/tarif isn't supported by these default values.
let containerList = ['Daten', 'D_EU_DATA', 'C_DIY_Data_National']
let codeList = ['-1', '-5' ,'45500', '40100']

////////////////////////////////////////////////////////////////////////////////
//////////////////////////         Dev Settings         ////////////////////////
////////////////////////////////////////////////////////////////////////////////

const debug = false
config.widgetFamily = config.widgetFamily || 'small'

////////////////////////////////////////////////////////////////////////////////
//////////////////////////         System-Config         ///////////////////////
////////////////////////////////////////////////////////////////////////////////

// Input
let widgetInputRAW = args.widgetParameter;

let widgetInput = null;
let user, pass, number, json
if (widgetInputRAW !== null) {
  const parameter = widgetInputRAW.toString().split("|")
  if(parameter.length > 1) {
    [user, pass, number, json] = parameter;

    if (!user || !pass || !number) {
      throw new Error("Invalid Widget parameter. Expected format: username|password|phonenumber")
    }
    if (/^49[\d]{5,}/.test(number) === false) {
      throw new Error("Invalid phonenumber format. Expected format: 491721234567")
    }
  } else {
    json = parameter
  }
  if (json) {
    try {
      const c = JSON.parse(json)
      
      containerList = c.containerList || containerList
      codeList = c.codeList || codeList
      darkModeSupport = c.darkModeSupport !== undefined ? c.darkModeSupport : darkModeSupport
      showRemainingContingent = c.showRemainingContingent !== undefined ? c.showRemainingContingent : showRemainingContingent  
      showRemainingDaysAsProgressbar = c.showRemainingDaysAsProgressbar !== undefined ? c.showRemainingDaysAsProgressbar : showRemainingDaysAsProgressbar
    } catch (error) {
      console.log('Faild to extract JSON-config. Fallback to default config')
    }
  }
} else if (!config.runsInWidget && config.runsInApp) {  
  const prompt = new Alert()
  prompt.message = 'Möchtest du den Setup Assistant starten?'
  prompt.addAction('Ja')
  prompt.addCancelAction('Nein')
  
  if (await prompt.presentAlert() === 0) {
    await setupAssistant()
  }
  return Script.complete()
}

// Text sizes
const fontSizeData = 11
const lineNumberData = 1
const minimumScaleFactor = 0.8 // Value between 1.0 and 0.1

// Number of data by Size
const numberOfDisplayedDataBySize = {  
  small: 1,
  medium: 2,
  large: 4
}

// Progressbar
const h = 5
let width
if (config.widgetFamily === 'small') {
  width = 200
} else {
  width = 400
}

// Colors
let backColor = new Color('D32D1F')
let backColor2 = new Color('93291E')
let textColor = new Color('EDEDED')
let strokeColor = new Color('B0B0B0')
let fillColor = new Color('EDEDED')
let strokeColorProgressbar = new Color('EDEDED')
let fillColorProgressbar = new Color('B0B0B0')

if (darkModeSupport) {  
  backColor = Color.dynamic(backColor, new Color('111111'))
  backColor2 = Color.dynamic(backColor2, new Color('222222'))
  textColor = Color.dynamic(textColor, new Color('EDEDED'))
  strokeColor = Color.dynamic(strokeColor, new Color('111111'))
  fillColor = Color.dynamic(fillColor, new Color('EDEDED'))
  strokeColorProgressbar = Color.dynamic(strokeColorProgressbar, new Color('EDEDED'))
  fillColorProgressbar = Color.dynamic(fillColorProgressbar, new Color('111111'))
}
////////////////////////////////////////////////////////////////////////////////

async function setupAssistant () {
  let parameter = ''
  
  const promptLoginType = new Alert()
  promptLoginType.message = 'Welche Login-Methode möchtest du verwenden?'
  promptLoginType.addAction('Netzwerk-Login')
  promptLoginType.addAction('MeinVodafone-Login')    
  
  let cookies, msisdn
  if (await promptLoginType.presentAlert()  === 0) {
    const promptWlanNotice = new Alert()
    promptWlanNotice.title = 'Hinweis'
    promptWlanNotice.message = 'Für diese Login-Methode muss das WLAN deaktiviert sein.'
    promptWlanNotice.addAction('WLAN ist deaktiviert')
    await promptWlanNotice.presentAlert()
    
    try {
      let { cookies: c, msisdn: m } = await getSessionCookiesViaNetworkLogin()
      cookies = c
      msisdn = m
    } catch (error) {
      const promptError = new Alert()
      promptError.title = 'Login fehlgeschlagen'
      promptError.message = 'Der Login ist fehlgeschlagen. Bitte prüfe ob eine Mobilfunk-Verbindung vorhanden ist. Weitere Details findest du in den Logs.'  
      promptError.addAction('Schließen')
      await promptError.present()  

      throw error
    }
  } else {
    const promptCredentails = new Alert()
    promptCredentails.title = 'Zugangsdaten'
    promptCredentails.message = 'Bitte gebe deine MeinVodafone-Zugangsdaten und deine Rufnummer ein:'  
    promptCredentails.addTextField('Benutzernamen')
    promptCredentails.addSecureTextField('Passwort')
    promptCredentails.addTextField('Rufnummer')
    promptCredentails.addAction('Weiter')
    
    
    await promptCredentails.present()
    const user = promptCredentails.textFieldValue(0).trim()
    const pass = promptCredentails.textFieldValue(1).trim()
    const number = promptCredentails.textFieldValue(2).replace(/^0/, '49').trim()
    try {
      let { cookies: c } = await getSessionCookiesViaMeinVodafoneLogin(user, pass)
      cookies = c
      msisdn = number
    } catch (error) {
      console.error(error)
    }
    parameter = `${user}|${pass}|${msisdn}`
  }
  let CookieValues = cookies.map(function (v) {
    return v.name + "=" + v.value
  })
  let req
  req = new Request(`https://www.vodafone.de/api/enterprise-resources/core/bss/sub-nil/mobile/payment/service-usages/subscriptions/${msisdn}/unbilled-usage`)
  req.headers = {
    'x-vf-api': '1499082775305',
    'Referer': 'https://www.vodafone.de/meinvodafone/services/',
    'Accept': 'application/json',
    'Cookies': CookieValues.join(';')
  }
  let res, data
  try {
    res = await req.loadJSON()
    if(!res['serviceUsageVBO'] || !res['serviceUsageVBO']['usageAccounts'] || !res['serviceUsageVBO']['usageAccounts'][0] || !res['serviceUsageVBO']['usageAccounts'][0]['usageGroup']) {
      throw new Error('invalid response: ' + JSON.stringify(res))
    }
    data = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup']
  } catch (error) {
      const promptError = new Alert()
      promptError.title = 'Laden der Daten fehlgeschlagen'
      promptError.message = 'Das Laden der Vertragsdaten ist fehlgeschlagen. Dies kann verschiedene Gründe haben. Bitte prüfen die Logs für weitere Informationen'  
      promptError.addAction('Schließen')
      await promptError.present()
      throw error
  }
  
  const list = data.map(function (o) {
    return o.usage.map(function(i) {
      i.container = o.container || null
      i.selected = false
      return i
    }).filter(x => x.code)
  }).flat()
  
  const promptBeforeTable = new Alert()
  promptBeforeTable.title = 'Hinweis'
  promptBeforeTable.message = 'Dein Vertrag wurde analysiert. Bitte wähle im nachfolgenden Dialog die Daten aus, die du im Widget anzeigen möchtest.'
  promptBeforeTable.addAction('Weiter')
  await promptBeforeTable.present()
  
  const table = new UITable()
  table.showSeparators = true
  
  function populateTable() {
    table.removeAllRows()
    
    for (i = 0; i < list.length; i++) {
      let row = new UITableRow()
      row.dismissOnSelect = false
      
      let selectedCell = row.addText((list[i].selected)? "✓" : "")  
      selectedCell.widthWeight = 5
      
      let textCell = row.addText(list[i].name)
      textCell.widthWeight = 70
      
      row.onSelect = (number) => {
        list[number].selected = !list[number].selected
        populateTable()
        table.reload()
      }
      table.addRow(row)
    }
  }
  populateTable()
  await QuickLook.present(table)
  
  const selectedList = list.filter(x => x.selected)
  const containerList = [...new Set(selectedList.map(x => x.container))]
  const codeList = [...new Set(selectedList.map(x => x.code))]
  
  const options = {
    containerList,
    codeList
  }
  
  const promptDarkMode = new Alert()
  promptDarkMode.title = 'Dark Mode Unterstützung'
  promptDarkMode.message = 'Möchtest du die Dark Mode Unterstützung aktivieren oder deaktivieren? Wenn die Dark Mode Unterstützung deaktiviert ist, bleibt das Widget immer rot.'  
  promptDarkMode.addAction('Aktivieren')
  promptDarkMode.addDestructiveAction('Deaktivieren')
  
  options.darkModeSupport = await promptDarkMode.present() === 0 ? true : false
  
  const promptRemainingContingent = new Alert()
  promptRemainingContingent.title = 'Anzeige Option'
  promptRemainingContingent.message = 'Möchtest du das verbleibende oder verwendte Kontigent angezeigt bekommen?'  
  promptRemainingContingent.addAction('verbleibende Kontigent')
  promptRemainingContingent.addAction('verwendte Kontigent')
  
  options.showRemainingContingent = await promptRemainingContingent.present() === 0 ? true : false
  
  const promptRemainingDaysAsProgressbar = new Alert()
  promptRemainingDaysAsProgressbar.title = 'Fortschrittsbalken für verbleibenden Tage'
  promptRemainingDaysAsProgressbar.message = 'Möchtest du dass ein Fortschrittsbalken für die verbleibenden Tage angezeigt wird?'  
  promptRemainingDaysAsProgressbar.addAction('Anzeigen')
  promptRemainingDaysAsProgressbar.addDestructiveAction('Nicht anzeigen')
  
  options.showRemainingDaysAsProgressbar = await promptRemainingDaysAsProgressbar.present() === 0 ? true : false
  
  parameter += `|${JSON.stringify(options)}`
  parameter = parameter.replace(/^\|/, '')
  console.log('Config: ' + parameter)
  Pasteboard.copy(parameter)
  const promptSuccess = new Alert()
  promptSuccess.title = 'Setup abgeschlossen'
  promptSuccess.message = 'Die für dich passende Konfiguration wurde generiert und in die Zwischenablage kopiert.\nFüge diese nun in das Feld "Parameter" in den Widget Einstellungen ein.'
  promptSuccess.addAction('Schließen')
  await promptSuccess.present()
} 

function creatProgress(total, havegone) {
  const context = new DrawContext()
  context.size = new Size(width, h)
  context.opaque = false
  context.respectScreenScale = false
  
  // Background Path
  context.setFillColor(fillColorProgressbar)
  const path = new Path()
  path.addRoundedRect(new Rect(0, 0, width, h), 3, 2)
  context.addPath(path)
  context.fillPath()
  
  // Progress Path
  context.setFillColor(strokeColorProgressbar)  
  const path1 = new Path()
  const path1width = (width * (havegone / total) > width) ? width : width * (havegone / total)
  path1.addRoundedRect(new Rect(0, 0, path1width, h), 3, 2)
  context.addPath(path1)
  context.fillPath()
  return context.getImage()
}

function getDiagram(percentage) {
  function drawArc(ctr, rad, w, deg) {
    bgx = ctr.x - rad
    bgy = ctr.y - rad
    bgd = 2 * rad
    bgr = new Rect(bgx, bgy, bgd, bgd)
  
    canvas.setFillColor(fillColor)
    canvas.setStrokeColor(strokeColor)
    canvas.setLineWidth(w)
    canvas.strokeEllipse(bgr)
  
    for (t = 0; t < deg; t++) {
      rect_x = ctr.x + rad * sinDeg(t) - w / 2
      rect_y = ctr.y - rad * cosDeg(t) - w / 2
      rect_r = new Rect(rect_x, rect_y, w, w)
      canvas.fillEllipse(rect_r)
    }
  }
  function sinDeg(deg) {
    return Math.sin((deg * Math.PI) / 180)
  }
  
  function cosDeg(deg) {
    return Math.cos((deg * Math.PI) / 180)
  }
  const canvas = new DrawContext()
  const canvSize = 200
  const canvTextSize = 36
  
  const canvWidth = 10
  const canvRadius = 80
  
  canvas.opaque = false  
  canvas.size = new Size(canvSize, canvSize)
  canvas.respectScreenScale = true
    
  drawArc(
    new Point(canvSize / 2, canvSize / 2),
    canvRadius,
    canvWidth,
    Math.floor(percentage * 3.6)
  )

  const canvTextRect = new Rect(
    0,
    100 - canvTextSize / 2,
    canvSize,
    canvTextSize
  )
  canvas.setTextAlignedCenter()
  canvas.setTextColor(textColor)
  canvas.setFont(Font.boldSystemFont(canvTextSize))
  canvas.drawTextInRect(`${percentage}%`, canvTextRect)

  return canvas.getImage()
}

function getTimeRemaining(endtime) {
  const total = Date.parse(endtime) - Date.parse(new Date())
  const seconds = Math.floor((total / 1000) % 60)
  const minutes = Math.floor((total / 1000 / 60) % 60)
  const hours = Math.floor((total / (1000 * 60 * 60)) % 24)
  const days = Math.floor(total / (1000 * 60 * 60 * 24))

  return {
    total,
    days,
    hours,
    minutes,
    seconds
  }
}

async function getSessionCookiesViaNetworkLogin() {
  let req;
  req = new Request("https://www.vodafone.de/mint/rest/session/start")
  req.method = "POST";
  req.headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }

  req.body = JSON.stringify({
    "authMethod": "AAA",
    "byPIN": false,
    "additionalParameters": {
      "deviceType": "Smartphone"
    }
  })
  try {
    let res = await req.loadJSON()
    return { cookies: req.response.cookies, msisdn: res.msisdn }
  } catch (e) {
    console.log("Login failed! Please check if Wifi is disabled.")
    throw new Error(`Login failed with HTTP-Status-Code ${req.response.statusCode}`)
  }
};

async function getSessionCookiesViaMeinVodafoneLogin(u, p) {
  let req;
  req = new Request("https://www.vodafone.de/mint/rest/session/start")
  req.method = "POST";
  req.headers = {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }

  req.body = JSON.stringify({
    "clientType": "Portal",
    "username": u,
    "password": p,
  })
  try {
    let res = await req.loadJSON()
    return { cookies: req.response.cookies }
  } catch (e) {
    console.log("Login failed!")
    throw new Error(`Login failed with HTTP-Status-Code ${req.response.statusCode}`)
  }
};

async function getUsage(user, pass, number) {
  let cookies, msisdn
  if (user && pass && number) {
    console.log("Login via MeinVodafone")
    let { cookies: c } = await getSessionCookiesViaMeinVodafoneLogin(user, pass)
    cookies = c
    msisdn = number
  } else {
    console.log("Login via Network")
    let { cookies: c, msisdn: m } = await getSessionCookiesViaNetworkLogin()
    cookies = c
    msisdn = m
  }
  let CookieValues = cookies.map(function (v) {
    return v.name + "=" + v.value
  })
  let req
  req = new Request(`https://www.vodafone.de/api/enterprise-resources/core/bss/sub-nil/mobile/payment/service-usages/subscriptions/${msisdn}/unbilled-usage`)
  req.headers = {
    'x-vf-api': '1499082775305',
    'Referer': 'https://www.vodafone.de/meinvodafone/services/',
    'Accept': 'application/json',
    'Cookies': CookieValues.join(';')
  }
  try {
    let res = await req.loadJSON()
    if(!res['serviceUsageVBO'] || !res['serviceUsageVBO']['usageAccounts'] || !res['serviceUsageVBO']['usageAccounts'][0]) {
      if (debug) {
        console.log(JSON.stringify(res, null, 2))
      }
      
    }
    console.log("unbilled-usage loaded")
    if (debug) {
      console.log(JSON.stringify(res['serviceUsageVBO']['usageAccounts'][0], null, 2))
    }
    const marketCode = res['serviceUsageVBO']['usageAccounts'][0]['details']['marketCode']
    const billDate = res['serviceUsageVBO']['usageAccounts'][0]['details']['billDate']
    const amount = res['serviceUsageVBO']['usageAccounts'][0]['details']['amount']
    
    let usage = []
    
    // Get Main Container
    let container = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup'].filter(function (v) {
      return containerList.includes(v.container)
    })  

    if (container.length === 0) {
      const ErrorMsg = "Can't find usageGroup with supported Container: " + containerList.join(', ') + ".";
      console.log(ErrorMsg)

      const listOfContainerNamesInResponse = res['serviceUsageVBO']['usageAccounts'][0]['usageGroup'].map(function (v) {
        return v.container
      })
      console.log("Please check the following list to find the correct container name for your case and adjust the list of container names at the beginnging: " + listOfContainerNamesInResponse.join(",  "))
      throw new Error(ErrorMsg)
    }

  
    for(let i = 0; i < container.length; i++) {
      for (let j = 0; j < container[i]['usage'].length; j++) {
        if (codeList.includes(container[i]['usage'][j]['code'])) {
          usage.push(container[i]['usage'][j])
        }
      }
    }
    if (usage.length === 0) {
      const ErrorMsg = "Can't find Usage with supported Codes: " + codeList.join(', ') + ".";
      console.log(ErrorMsg)  
       
      const listOfCodeInResponse = []
      for(let i = 0; i < container.length; i++) {
        for (let j = 0; j < container[i]['usage'].length; j++) {
          listOfCodeInResponse.push(`Code: "${container[i]['usage'][j].code}" for "${container[i]['usage'][j].description}"`)
        }
      }
      console.log("Please check the following list to find the correct code for your case and adjust the list of codes at the beginnging: " + listOfCodeInResponse.join(", "))
      throw new Error(ErrorMsg)
    }
    
    let endDate = usage[0].endDate
    if (endDate == null) {
      endDate = res['serviceUsageVBO']['billDetails']['billCycleEndDate'] || null
    }
    
    return {
      billDate,
      endDate,
      amount,
      marketCode,
      usage
    }
  } catch (e) {
    console.log("Loading usage data failed")
    throw e
  }
};

var today = new Date()

// Set up the file manager.
const files = FileManager.local()

// Set up cache
const cacheNamePostfix = (number) ? number.substr(number.length - 4) : 'networkLogin'
const cachePath = files.joinPath(files.documentsDirectory(), "widget-vodafone-" + cacheNamePostfix)
const cacheExists = files.fileExists(cachePath)
const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0

// Get Data
let data
let lastUpdate
try {
  // If cache exists and it's been less than 30 minutes since last request, use cached data.
  if (cacheExists && (today.getTime() - cacheDate.getTime()) < (cacheMinutes * 60 * 1000)) {
    console.log("Get from Cache")
    data = JSON.parse(files.readString(cachePath))
    lastUpdate = cacheDate
  } else {
    console.log("Get from API")
    data = await getUsage(user, pass, number)
    console.log("Write Data to Cache")
    try {
      files.writeString(cachePath, JSON.stringify(data))
    } catch (e) {
      console.log("Creating Cache failed!")
      console.log(e)
    }

    lastUpdate = today
  }
} catch (e) {
  console.error(e)
  if (cacheExists) {
    console.log("Get from Cache")
    data = JSON.parse(files.readString(cachePath))
    lastUpdate = cacheDate
  } else {
    console.log("No fallback to cache possible. Due to missing cache.")
  }
}

// Create Widget
let widget = new ListWidget();

widget.setPadding(10, 10, 10, 10)

if (data !== undefined) {
  if(debug) {
      console.log(JSON.stringify(data, null, 2))
  }
  const gradient = new LinearGradient()
  gradient.locations = [0, 1]
  gradient.colors = [
    backColor,
    backColor2
  ]
  widget.backgroundGradient = gradient

  let firstLineStack = widget.addStack()

  let provider = firstLineStack.addText("Vodafone")
  provider.font = Font.mediumSystemFont(12)
  provider.textColor = textColor
  
  if (data.marketCode === 'MMO') {
    widget.addSpacer(2)
    const amount = widget.addText(`Guthaben: ${data.amount.replace('.', ',')} €`)
    amount.font = Font.systemFont(8)
    amount.textColor = textColor
  }

  // Last Update
  firstLineStack.addSpacer()
  let lastUpdateText = firstLineStack.addDate(lastUpdate)
  lastUpdateText.font = Font.systemFont(8)
  lastUpdateText.rightAlignText()
  lastUpdateText.applyTimeStyle()
  lastUpdateText.textColor = Color.lightGray()

  widget.addSpacer()
  
  const stack = widget.addStack()
  stack.layoutHorizontally()
  
  let i = 0
  let row
  data.usage.slice(0, numberOfDisplayedDataBySize[config.widgetFamily]).forEach((v) => {
    if (++i % 2 == 1) {
      row = widget.addStack()
      row.layoutHorizontally()
      widget.addSpacer(5)
    }
    column = row.addStack()
    column.layoutVertically()
    column.centerAlignContent()
    
    const percentage = (100 / v.total * (showRemainingContingent ? v.remaining : v.used)).toFixed(0);
    const imageStack = column.addStack()
    imageStack.layoutHorizontally()
    imageStack.addSpacer()
    imageStack.addImage(getDiagram(percentage));
    imageStack.addSpacer()
    column.addSpacer(2)
    
    // Total Values
    let totalValues;
    if (v.unitOfMeasure !== 'MB') {
      totalValues = `${(showRemainingContingent ? v.remaining : v.used)} ${v.unitOfMeasure} von ${v.total} ${v.unitOfMeasure}`
    } else if (parseInt(v.total) < 1000) {
      totalValues = `${(showRemainingContingent ? v.remaining : v.used)} MB von ${v.total} MB`
    } else {
      let GB = ((showRemainingContingent ? v.remaining : v.used) / 1024).toFixed(2)
      let totalGB = (v.total / 1024).toFixed(2)
      totalValues = `${GB} GB von ${totalGB} GB`
    }
    textStack = column.addStack()
    textStack.layoutHorizontally()
    textStack.addSpacer()
    let diagramText = textStack.addText(totalValues)
    diagramText.font = Font.mediumSystemFont(fontSizeData)
    diagramText.minimumScaleFactor = minimumScaleFactor
    diagramText.lineLimit = lineNumberData
    diagramText.centerAlignText()
    diagramText.textColor = textColor
    textStack.addSpacer()
    
    nameStack = column.addStack()
    nameStack.layoutHorizontally()
    nameStack.addSpacer()
    let diagramName = nameStack.addText(v.name.replace('Inland & EU', '').trim())
    diagramName.font = Font.systemFont(fontSizeData - 1)
    diagramName.minimumScaleFactor = minimumScaleFactor
    diagramName.lineLimit = 1
    diagramName.centerAlignText()
    diagramName.textColor = textColor
    nameStack.addSpacer()
  })
  
  widget.addSpacer()

  // Remaining Days
  if (data.endDate) {
    widget.addSpacer(5)
    let remainingDays = getTimeRemaining(data.endDate).days + 2
    if(data.billDate && showRemainingDaysAsProgressbar) {
      const startDate = new Date(data.billDate)
      const endDate = new Date(data.endDate)
      const total = (endDate - startDate) / (1000 * 60 * 60 * 24)
      
      const progressBarStack = widget.addStack()
      progressBarStack.layoutHorizontally()
      progressBarStack.addSpacer()
      
      const progressBar = progressBarStack.addImage(creatProgress(total, total - remainingDays))
      progressBarStack.addSpacer()
      widget.addSpacer(4)
    }

    let remainingDaysText = widget.addText(`${remainingDays} Tage verbleibend`)
    remainingDaysText.font = Font.systemFont(8)
    remainingDaysText.centerAlignText()
    remainingDaysText.textColor = textColor
  }

} else {
  let fallbackText = widget.addText("Es ist ein Fehler aufgetreten! Bitte prüfen Sie die Logs direkt in der App.")
  fallbackText.font = Font.mediumSystemFont(12)
  fallbackText.textColor = textColor
}

if (!config.runsInWidget) {
  switch (config.widgetFamily) {
    case 'small': await widget.presentSmall(); break;
    case 'medium': await widget.presentMedium(); break;
    case 'large': await widget.presentLarge(); break;
  }
  
} else {
  // Tell the system to show the widget.
  Script.setWidget(widget)
  Script.complete()
}