import sys
sys.path.append('..')

import collections

import mido

from commons import dgxdump
from commons.dumpdata import messages, songdata, regdata, regvalues

old_syx_messages = mido.read_syx_file('../data/syxout5.syx')
clear_syx_messages = mido.read_syx_file('../data/clear_bulk.txt')

o_dump = dgxdump.DgxDump(old_syx_messages)
c_dump = dgxdump.DgxDump(clear_syx_messages)

# songs slices
songslices = collections.OrderedDict([
('songs', slice(0x00, 0x01)),
('mystery', slice(0x01, 0x15D)),
('tracks', slice(0x15D, 0x167)),
('durations',  slice(0x167, 0x17B)),
('trackdurations', slice(0x17B, 0x1F3)),
('presetstyle', slice(0x1F3, 0x22F)),
('beginningblocks', slice(0x22F, 0x24D)),
('nextblocks', slice(0x24D, 0x2CF)),
('startmarker', slice(0x2CF, 0x2D5)),
('blockdata', slice(0x2D5, 0x106D5)),
('endmarker', slice(0x106D5, None)),
])
EXPECTED_SIZE = 0x106DB

PRESETSTYLE = b'PresetStyle\0'*5
MARKER = b'PK0001'

def hex_string(data):
    return " ".join("{:02X}".format(b) for b in data)

def bin_string(data):
    return " ".join("{:08b}".format(b) for b in data)

def line_hex(data, head=None, tail=0):
    if head is None:
        head = len(data)
    tailstart = len(data) - tail
    if tailstart <= head:
        return (hex_string(data))
    else:
        return ("{} .. {}".format(hex_string(data[:head]), hex_string(data[tailstart:])))
    
def song_section(dump, section):
    return dump.song_data.data[songslices[section]]

for sec in songslices:
    print(sec)
    print(line_hex(song_section(o_dump, sec), 32, 4))
    print(line_hex(song_section(c_dump, sec), 32, 4))

song_section(o_dump, 'mystery') == song_section(c_dump, 'mystery')

"""
Explanation: The big reset
So I went ahead and cleared the memory.
End of explanation
"""


all(b==0 for b in song_section(c_dump, 'nextblocks'))

all(b==0 for b in song_section(c_dump, 'blockdata'))

"""
Explanation: The mystery section remains the same.
End of explanation
"""


bytes(song_section(c_dump, 'presetstyle'))

"""
Explanation: All the blocks are empty.
End of explanation
"""


print(line_hex(o_dump.reg_data.data, 32, 4))
print(line_hex(c_dump.reg_data.data, 32, 4))

for bank in range(1, 8+1):
    for button in range(1, 2+1):
        print(bank, button)
        print(line_hex(o_dump.reg_data.settings.get_setting(bank, button).data))
        print(line_hex(c_dump.reg_data.settings.get_setting(bank, button).data))

"""
Explanation: The 'PresetStyle' settings are empty, too.
End of explanation
"""


for x in range(2, 7):
    !diff -qs ../data/backup_experiment/cb1.txt ../data/backup_experiment/cb{x}.txt
!diff -qs ../data/backup_experiment/cb1.txt ../data/clear_bulk.txt

c2_syx_messages = mido.read_syx_file('../data/backup_experiment/cb1.txt')
c2_dump = dgxdump.DgxDump(c2_syx_messages)


c_dump.song_data.data == c2_dump.song_data.data

c_dump.reg_data.data == c2_dump.reg_data.data

for sec in songslices:
    c_sec = song_section(c_dump, sec)
    c2_sec = song_section(c2_dump, sec)
    if c_sec != c2_sec:
        print(sec)
        print(line_hex(c_sec, 32, 4))
        print(line_hex(c2_sec, 32, 4))

for n, (a, b) in enumerate(zip(c_dump.song_data.data, c2_dump.song_data.data)):
    if a != b:
        print("{0:02X}: {1:02X} {2:02X} ({1:03d} {2:03d})".format(n, a, b))

"""
Explanation: Each of the registry settings are completely blank.
Interesting things to note: the first byte is 0 instead of 1, which probably indicates that the setting is unused.
The bytes that were FF in each recorded setting are 00 here.
Investigating FUNCTION backup
According to the manual (page 49), the following settings can be saved to backup, i.e. persistent memory for startup bu holding the FUNCTION button:

User songs (These are saved when recorded anyway)
Style files (the ones loaded using SmartMedia)
Touch response (ON/OFF)
Registration memory
These function settings:
Tuning
Split point
Touch sensitivity
Style volume
Song volume
Metronome volume
Grade
Demo cancel
Language
Media Select
Panel Sustain.

These backup settings are also cleared with the rest of the memory.
The default values for these settings are as follows:
| setting           | default      |
|-------------------|--------------|
| Touch response    | ON           |
| Tuning            | 000          |
| Split point       | 54 (F#2)     |
| Touch sensitivity | 2 (Medium)   |
| Style volume      | 100          |
| Song volume       | 100          |
| Metronome volume  | 100          |
| Grade             | ON           |
| Demo cancel       | OFF          |
| Language          | English      |
| Media Select      | Flash Memory |
| Panel sustain     | OFF          |
As an experiment, I changed the values of the function settings:
| setting           | new value    |
|-------------------|--------------|
| Touch response    | ON           |
| Tuning            | 057          |
| Split point       | 112 (E7)     |
| Touch sensitivity | 3 (Hard)     |
| Style volume      | 045          |
| Song volume       | 079          |
| Metronome volume  | 121          |
| Grade             | OFF          |
| Demo cancel       | ON           |
| Language          | Japanese     |
| Media Select      | Smart Media  |
| Panel sustain     | ON           |
and without making a backup:
 - took a bulk dump. (cb1.txt),
 - then made the backup, took another bulk dump, (cb2.txt),
 - restarted with the new settings, took another (cb3.txt),
 - reset everything to default without backup (cb4.txt),
 - made a backup again and took another dump (cb5.txt),
 - then restarted again (cb6.txt).
All of these files were identical to each other, which suggests that these backup settings are not stored any part we can retrieve.
However, there is one thing interesting about these files, in that they differ from the dump I got immediately after resetting the memory (clear_bulk.txt).
End of explanation
"""


r1_dump = dgxdump.DgxDump(mido.read_syx_file('../data/post_clear/1reg.syx'))

c2_dump.song_data.data == r1_dump.song_data.data

c2_dump.reg_data.data == r1_dump.reg_data.data

for bank in range(1, 8+1):
    for button in range(1, 2+1):
        if not all(x == 0 for x in r1_dump.reg_data.settings.get_setting(bank, button).data):
            print(bank, button)

line_hex(r1_dump.reg_data.settings.get_setting(3, 2).data)

for bb in [(3, 2), (1, 1)]:
    sets = r1_dump.reg_data.settings.get_setting(*bb)
    print(line_hex(sets.data))
    sets.print_settings()
    sets.print_unusual()

"""
Explanation: The only difference seems to be two bytes in the mystery section, at offsets 0x07 and 0x08.
Perhaps this has to do with some kind of internal wear levelling or something.
Registration extension
Now that the memory has been cleared, we can hopefully figure out more about the registration settings.
Recording Bank 3, Button 2 as the following settings:
| setting          | value |
|------------------|-------|
| Style            |   092 |
| Accompaniment    |    ON |
| Split point      |   053 |
| Main A/B         |     A |
| Style vol        |   050 |
| Main voice       |   060 |
| Main Octave      |    -1 |
| Main Volume      |   054 |
| Main Pan         |   092 |
| Main Reverb      |   078 |
| Main Chorus      |   103 |
| Split            |    ON |
| Split voice      |   003 |
| Split Octave     |     0 |
| Split Volume     |   108 |
| Split Pan        |   064 |
| Split Reverb     |   032 |
| Split Chorus     |   127 |
| Dual             |   OFF |
| Dual voice       |   201 |
| Dual Octave      |    +2 |
| Dual Volume      |   095 |
| Dual Pan         |   048 |
| Dual Reverb      |   017 |
| Dual Chorus      |   082 |
| Pitch bend range |    05 |
| Reverb type      |    --(Room)     |
| Chorus type      |    --(Celeste)  |
| Harmony          |   OFF           |
| Harmony type     |    06(Trill1/4) |
| Harmony volume   |   085/---*      |
| Transpose        |   +03           |
| Tempo            |   080           |
| Panel Sustain    |    ON           |
*This was set using a different Harmony type setting.
End of explanation
"""


r2_dump = dgxdump.DgxDump(mido.read_syx_file('../data/post_clear/2reg.txt'))
sets = r2_dump.reg_data.settings.get_setting(2,2)
sets.print_settings()
sets.print_unusual()

"""
Explanation: I believe the only real way to get unrecorded settings is to reset the memory, which clears all the values to zero.
This means that the first byte which has a value of 01 for all recorded settings can indeed be used as a flag... along with the FF byte at offset 24, and any other setting that cannot be set to a value of zero, such as the Pitch Bend range, Reverb type, Chorus type, and panel Sustain.
Personally, I think it makes more sense for the first byte to act as the recorded flag, so I think I'll use that.
End of explanation
"""


