class TestMajorMajorHelpers:

    def setup_method(self, method):
        self.collab0 = MajorMajor()
        
    def test_new_document(self):
        # leaving nothing specified
        doc = self.collab0.new_document()
        assert isinstance(doc, Document)
        assert doc.get_snapshot() == {}
    def glue_majormajor(self):

        i_id = self.textbuffer.connect('insert-text', self.insert_text_handler)
        d_id = self.textbuffer.connect('delete-range', self.delete_range_handler)
        self.majormajor_handlers = {'insert-text':i_id, 'delete-range':d_id}


        self.majormajor = MajorMajor()

        self.majormajor.connect('remote-cursor-update', self.remote_cursor_update)
        self.majormajor.connect('receive-changeset', self.receive_changeset)
        self.majormajor.connect('receive-snapshot', self.receive_snapshot)
        self.majormajor.connect('accept-invitation', self.accept_invitation)
        self.random_insert = False
        self.uncommitted_ops = []
        GObject.timeout_add(20, self.test_random_insert)
class TextViewWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="TextView Example")

        self.set_default_size(-1, 350)

        self.grid = Gtk.Grid()
        self.add(self.grid)

        self.create_textview()
        self.create_toolbar()
        self.create_buttons()

        self.glue_majormajor()
        #box = self.majormajor.get_contact_list_gui()
        #self.grid.attach(box, 4, 1, 1, 2)


    def glue_majormajor(self):

        i_id = self.textbuffer.connect('insert-text', self.insert_text_handler)
        d_id = self.textbuffer.connect('delete-range', self.delete_range_handler)
        self.majormajor_handlers = {'insert-text':i_id, 'delete-range':d_id}


        self.majormajor = MajorMajor()

        self.majormajor.connect('remote-cursor-update', self.remote_cursor_update)
        self.majormajor.connect('receive-changeset', self.receive_changeset)
        self.majormajor.connect('receive-snapshot', self.receive_snapshot)
        self.majormajor.connect('accept-invitation', self.accept_invitation)
        self.random_insert = False
        self.uncommitted_ops = []
        GObject.timeout_add(20, self.test_random_insert)

    def open_default_connection(self, listen_port):
        self.majormajor.open_default_connection(listen_port)
        self.document = self.majormajor.new_document(snapshot='')
        self.majormajor.announce()

    def open_mq_connection(self):
        self.majormajor.open_mq_connection()
        self.document = self.majormajor.new_document(snapshot='')
        self.majormajor.announce()

    def open_http_connection(self):
        self.majormajor.open_http_connection()
        self.document = self.majormajor.new_document(snapshot='')
        self.majormajor.announce()

    def accept_invitation(self, doc):
        self.document = doc

    def remote_cursor_update(self):
        buf = self.textbuffer
        for i in self.majormajor.connections:
            m = buf.get_mark(i.user)
            if m != None:
                buf.delete_mark(m)
            itr = buf.get_start_iter()
            itr.set_offset(i.cursor['offset'])
            m = buf.create_mark(i.user, itr)
            m.set_visible(True)

    def insert_text_handler(s, textbuffer, iter_, text, length):
        op = Op('si', [], offset=iter_.get_offset(), val=text)
        s.document.add_local_op(op)

    def delete_range_handler(s, textbuffer, start, end):
        val = end.get_offset() - start.get_offset()
        op = Op('sd', [], offset=start.get_offset(), val=val)
        s.document.add_local_op(op)

    def receive_changeset(self, opcodes):
        try:
            self.block_handlers()
            self.apply_opcodes(opcodes)
        finally:
            self.unblock_handlers()

    def apply_opcodes(self, opcodes):
        index = 0
        for op in opcodes:
            it = self.textbuffer.get_iter_at_offset(op[2] + index)
            if op[0] == 'insert':
                self.textbuffer.insert(it, op[3])
                index += len(op[3])
            elif op[0] == 'delete':
                end = self.textbuffer.get_iter_at_offset(op[2] + op[3] + index)
                self.textbuffer.delete(it, end)
                index -= op[3]
            elif op[0] == 'replace':
                end = self.textbuffer.get_iter_at_offset(op[2] + op[3] + index)
                self.textbuffer.delete(it, end)
                it = self.textbuffer.get_iter_at_offset(op[2] + index)
                self.textbuffer.insert(it, op[4])
                index += (len(op[4]) - op[3])

    def test_random_insert(self):
        """
        When the red button is pressed, random insert toggles on and off. When
        it is on, this will pick a random, valid op to add to the document.
        This will get called repeatedly on a timer to quickly fill the text
        buffer with gibberish and see if collaboration can keep up.
        """
        if not self.random_insert:
            return True
        doc = self.document
        old_state = doc.get_snapshot()
        n = random.randint(1, 5)
        o = random.randint(0, len(doc.get_snapshot()))
        if random.random() > .3 or len(doc.get_snapshot()) == 0:
            letters = [random.choice(string.ascii_letters + string.digits)
                       for x in range(n)]
            l = unicode(''.join(letters))
            doc.add_local_op(Op('si', [], offset=o, val=l))
        else:
            while o + n > len(doc.get_snapshot()):
                n -= 1
            if o == len(doc.get_snapshot()):
                o -= 1
                n = 1
            doc.add_local_op(Op('sd', [], offset=0, val=n))
        doc.close_changeset()
        opcodes = doc.get_diff_opcode(old_state)
        self.receive_changeset(opcodes)
        return True

    def receive_snapshot(self, snapshot):
        try:
            self.block_handlers()
            self.textbuffer.set_text(snapshot)
        finally:
            self.unblock_handlers()

    def block_handlers(self):
        h_ids = self.majormajor_handlers
        self.textbuffer.handler_block(h_ids['insert-text'])
        self.textbuffer.handler_block(h_ids['delete-range'])

    def unblock_handlers(self):
        h_ids = self.majormajor_handlers
        self.textbuffer.handler_unblock(h_ids['insert-text'])
        self.textbuffer.handler_unblock(h_ids['delete-range'])

    def create_toolbar(self):
        toolbar = Gtk.Toolbar()
        self.grid.attach(toolbar, 0, 0, 3, 1)

        button_save = Gtk.ToolButton.new_from_stock(Gtk.STOCK_SAVE)
        toolbar.insert(button_save, 0)

        button_random = Gtk.ToggleToolButton(Gtk.STOCK_MEDIA_RECORD)
        toolbar.insert(button_random, 1)

        button_drop_css = Gtk.ToggleToolButton(Gtk.STOCK_CANCEL)
        toolbar.insert(button_drop_css, 2)

        button_invite = Gtk.ToolButton()
        button_invite.set_stock_id(Gtk.STOCK_YES)
        toolbar.insert(button_invite, 3)

        button_save.connect("clicked", self.on_save_clicked)
        button_random.connect("clicked", self.on_random_clicked)
        button_drop_css.connect("clicked", self.on_drop_css_clicked)
        button_invite.connect("clicked", self.on_invite_clicked)



    def create_textview(self):
        scrolledwindow = Gtk.ScrolledWindow()
        scrolledwindow.set_hexpand(True)
        scrolledwindow.set_vexpand(True)
        self.grid.attach(scrolledwindow, 0, 1, 3, 1)

        self.textview = Gtk.TextView()
        self.textbuffer = self.textview.get_buffer()
        scrolledwindow.add(self.textview)

        self.textview.set_wrap_mode(True)

        self.tag_bold = self.textbuffer.create_tag("bold",
            weight=Pango.Weight.BOLD)
        self.tag_italic = self.textbuffer.create_tag("italic",
            style=Pango.Style.ITALIC)
        self.tag_underline = self.textbuffer.create_tag("underline",
            underline=Pango.Underline.SINGLE)
        self.tag_found = self.textbuffer.create_tag("found",
            background="yellow")

    def create_buttons(self):
        check_editable = Gtk.CheckButton("Editable")
        check_editable.set_active(True)
        check_editable.connect("toggled", self.on_editable_toggled)
        self.grid.attach(check_editable, 0, 2, 1, 1)

        check_cursor = Gtk.CheckButton("Cursor Visible")
        check_cursor.set_active(True)
        check_editable.connect("toggled", self.on_cursor_toggled)
        self.grid.attach_next_to(check_cursor, check_editable,
            Gtk.PositionType.RIGHT, 1, 1)

        radio_wrapnone = Gtk.RadioButton.new_with_label_from_widget(None,
            "No Wrapping")
        self.grid.attach(radio_wrapnone, 0, 3, 1, 1)

        radio_wrapchar = Gtk.RadioButton.new_with_label_from_widget(
            radio_wrapnone, "Character Wrapping")
        self.grid.attach_next_to(radio_wrapchar, radio_wrapnone,
            Gtk.PositionType.RIGHT, 1, 1)

        radio_wrapword = Gtk.RadioButton.new_with_label_from_widget(
            radio_wrapnone, "Word Wrapping")
        self.grid.attach_next_to(radio_wrapword, radio_wrapchar,
            Gtk.PositionType.RIGHT, 1, 1)

        radio_wrapnone.connect("toggled", self.on_wrap_toggled, Gtk.WrapMode.NONE)
        radio_wrapchar.connect("toggled", self.on_wrap_toggled, Gtk.WrapMode.CHAR)
        radio_wrapword.connect("toggled", self.on_wrap_toggled, Gtk.WrapMode.WORD)

    def on_save_clicked(self, widget):
        #self.document.ot()
        #self.document.rebuild_snapshot()
        import random
        n = "".join([random.choice("abcdef") for x in range(3)])
        n = "buffer-" + n + ".txt"
        f = open(n, 'w')
        #start = self.textbuffer.get_start_iter()
        #end = self.textbuffer.get_end_iter()
        #f.write(self.textbuffer.get_text(start, end,True))
        f.write(self.document.get_snapshot())
        ol = self.document.get_ordered_changesets()
        for cs in ol:
            f.write("\nCS ID: "+cs.get_id()[:8]+'\n')
            p_ids = [parent.get_id() for parent in cs.get_parents()]
            p_ids.sort()
            f.write("  PARETNS, "+", ".join(p_ids))
            f.write("\nOPS")
            for op in cs.get_ops():
                f.write("     TOffset " + str(op.t_offset))
                f.write("     Offset " + str(op.offset))
                f.write("     TVal "+str(op.t_val))
                f.write("     Val "+str(op.val))
                f.write("\n")
            f.write("\n")
            for ucs in cs.get_unaccounted_changesets():
                f.write("    " + ucs.get_id()[:8])
                #if ucs.user == self.majormajor.default_user:
                #    f.write("  <---- local")
                f.write("\n")
        f.close()

    def on_random_clicked(self, widget):
        self.random_insert = not self.random_insert

    def on_drop_css_clicked(self, widget):
        b = self.majormajor.drop_random_css
        self.majormajor.drop_random_css = not b

    def on_invite_clicked(self, widget):
        self.majormajor.invite_all(self.document)

    def on_button_clicked(self, widget, tag):
        self.majormajor.big_insert = not self.majormajor.big_insert

        bounds = self.textbuffer.get_selection_bounds()
        if len(bounds) != 0:
            start, end = bounds
            self.textbuffer.apply_tag(tag, start, end)

    def on_clear_clicked(self, widget):
        start = self.textbuffer.get_start_iter()
        end = self.textbuffer.get_end_iter()
        self.textbuffer.remove_all_tags(start, end)

    def on_editable_toggled(self, widget):
        self.textview.set_editable(widget.get_active())

    def on_cursor_toggled(self, widget):
        self.textview.set_cursor_visible(widget.get_active())

    def on_wrap_toggled(self, widget, mode):
        self.majormajor.big_insert = not self.majormajor.big_insert
        self.textview.set_wrap_mode(mode)

    def on_justify_toggled(self, widget, justification):
        self.textview.set_justification(justification)

    def win_shutdown(self, arg1, arg2):
        self.majormajor.shutdown()
        Gtk.main_quit()
 def setup_method(self, method):
     self.collab0 = MajorMajor()