#!/usr/bin/env python3

import os
import sys


def die(msg):
  print("ERROR:", msg)
  sys.exit(1)


class LmoEntry:
  def __init__(self, key_id = 0, plural = 0, offset = 0, length = 0, val = None):
    self.key_id = key_id
    self.plural = plural
    self.offset = offset
    self.length = length
    self.val = val
    self.dup = 0


class Lmo:
  options = ""
  entries = []  # list of LmoEntry

  def __init__(self):
    self.options = ""
    self.entries = []

  def load_from_bin(self, filename):
    self.entries = []
    use_plural_num = False
    with open(filename, "rb") as file:
      data = file.read()
    table_offset = int.from_bytes(data[-4:], byteorder='big')
    #print("table_offset = 0x%X" % table_offset)
    off = table_offset
    while True:
      if off + 16 >= len(data):
        break
      entry = LmoEntry()
      entry.key_id = int.from_bytes(data[off   :off+4] , byteorder='big')
      entry.plural = int.from_bytes(data[off+4 :off+8] , byteorder='big') - 1
      entry.offset = int.from_bytes(data[off+8 :off+12], byteorder='big')
      entry.length = int.from_bytes(data[off+12:off+16], byteorder='big')
      entry.val = data[entry.offset:entry.offset+entry.length]
      #print("%08X %d %08X %d" % (entry.key_id, entry.plural, entry.offset, entry.length))
      if off == table_offset:
        if entry.key_id == 0 and entry.plural == -1:
          use_plural_num = True
      if use_plural_num:
        if entry.plural >= 10 or (off != table_offset and entry.plural < 0):
          die("Too many plural forms")
      else:
        entry.plural = 0  # older version LMO-files contain hash of value
      self.entries.append(entry)
      off += 16
    self.entries = sorted(self.entries, key=lambda x: x.offset)
    #self.dup_search()

  def dup_search(self):
    count = 0
    for i, ent in enumerate(self.entries):
      for k, ek in enumerate(self.entries):
        if ent.key_id == ek.key_id and ent.offset != ek.offset:
          ent.dup = 1
          count += 1
          break
    return count

  def dup_search2(self):
    count = 0
    for i, ent in enumerate(self.entries):
      if ent.dup != 1:
        continue    # skip unique entry
      val = ent.val.decode('utf-8')
      for k, ek in enumerate(self.entries):
        if ek.key_id != ent.key_id:
          continue
        if ek.offset == ent.offset:
          continue  # skip self
        if ek.dup == 1:
          vk = ek.val.decode('utf-8')
          if vk == val:
            ek.dup = 2
            count += 1
    return count

  def save_to_text(self, filename = None):
    txt = ""    
    if 'k' in self.options:
      entries = sorted(self.entries, key=lambda x: x.key_id)
    else:
      entries = sorted(self.entries, key=lambda x: x.offset)
    for i, ent in enumerate(self.entries):
      ent.dup = 0
    self.dup_search()
    if 'z' in self.options:
      self.dup_search2()
    for i, ent in enumerate(entries):
      if ent.dup == 2:
        continue  # skip dup2
      val = ent.val.decode('utf-8')
      val = val.replace('\\', '\\\\')
      val = val.replace('"', r'\"')      
      if ent.key_id == 0 and ent.plural == -1:
        val = val.replace('\n', r'\n')
        txt += 'msgid ""' + '\n'
        txt += 'msgstr ""' + '\n'
        txt += '"Project-Id-Version: LuCI: {}\\n"'.format("base") + '\n'
        txt += '"Language: {}\\n"'.format("en") + '\n'
        txt += '"MIME-Version: 1.0\\n"' + '\n'
        txt += '"Content-Type: text/plain; charset=UTF-8\\n"' + '\n'
        txt += '"Content-Transfer-Encoding: 8bit\\n"' + '\n'
        txt += '"Plural-Forms: {}\\n"'.format(val) + '\n'
        txt += '"X-Generator: Weblate 4.8.1-dev\\n"' + '\n'
        txt += '\n'
        continue
      prefix = ''
      #if ent.plural != 0:
      #  prefix = '[%d]' % ent.plural
      if ent.dup:
        txt += '# DUP' + '\n'
      txt += 'msgid 0x{}'.format("%08X" % ent.key_id) + '\n' 
      line_limit = 77
      val = val.replace('\r', '')
      if val.find('\n') >= 0:
        txt += 'msgstr{} ""'.format(prefix) + '\n'
        vlist = val.split('\n')
        for i, v in enumerate(vlist):          
          v = v + r'\n' if i+1 < len(vlist) else v
          if len(v) > 0:
            txt += '"{}"'.format(v) + '\n'
      elif val.find(r'\n') >= 0:
        txt += 'msgstr{} ""'.format(prefix) + '\n'
        vlist = val.split(r'\n')
        for i, v in enumerate(vlist):
          if len(v) > 0:
            txt += '"{}"'.format(v) + '\n'
      elif len(val) > line_limit - 10 and val.find(' ') > 0:
        txt += 'msgstr{} ""'.format(prefix) + '\n'
        wlist = val.split(' ')
        v = ""
        for i, word in enumerate(wlist):
          if i > 0:
            word = ' ' + word
          if len(v) + len(word) < line_limit or len(v) == 0:
            v += word
            continue
          txt += '"{}"'.format(v) + '\n'
          v = word
        if len(v) > 0:
          txt += '"{}"'.format(v) + '\n'
      else:
        txt += 'msgstr{} "{}"'.format(prefix, val) + '\n'
      txt += '\n'
    if filename:
      with open(filename, "wt", encoding='UTF-8', newline = "\n") as file:
        file.write(txt)
    return txt


if __name__ == "__main__":
  fn_inp = sys.argv[1]
  fn_out = sys.argv[2]
  lmo = Lmo()
  if len(sys.argv) > 3:
    lmo.options = sys.argv[3]
  lmo.load_from_bin(fn_inp)
  if not ('m' in lmo.options):
    lmo.save_to_text(fn_out)
    print('\nPO-file saved to "{}"'.format(fn_out))
    sys.exit(0)

  # Merge 2 lmo-files
  fn_inp2 = sys.argv[4]
  lmo2 = Lmo()
  lmo2.load_from_bin(fn_inp2)
  for i, ent in enumerate(lmo2.entries):
    dup = False
    for k, ek in enumerate(lmo.entries):
      if ek.key_id == ent.key_id:
        dup = True
        break
    if not dup:
      lmo.entries.append(ent)
  lmo.options = 'kz'
  lmo.save_to_text(fn_out)
  print('\nMerged PO-file saved to "{}"'.format(fn_out))
  
  '''
  fn_out = fn_inp
  import po2lmo
  lmo3 = po2lmo.Lmo(verbose = 0)
  lmo3.load_from_list(lmo.entries)
  lmo3.save_to_bin(fn_out)
  print('\nMerged LMO-file saved to "{}"'.format(fn_out))
  '''


