class MenuFactory(IContextMenuFactory):
    def __init__(self):
        self.invoker = None
        self.messages = None
        # self.load_icon()

    def createMenuItems(self, invoker):
        global immediate_data, compuracer_communication_lock
        self.invoker = invoker

        if not (invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST
                or invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST
                or invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TABLE
                or invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_PROXY_HISTORY):
            return None

        self.messages = self.invoker.getSelectedMessages()
        if self.messages is None:
            return None
        if len(self.messages) > 1:
            button_text = "Send {} requests to CompuRacer".format(
                len(self.messages))
        else:
            button_text = "Send request to CompuRacer"
        global racer_alive
        if not racer_alive:
            button_text += " (offline)"
        elif compuracer_communication_lock.locked():
            button_text += " (busy)"
        send_menu = JMenuItem(button_text,
                              actionPerformed=self.start_request_transmitter)
        option_menu = JCheckBoxMenuItem("Immediate mode",
                                        actionPerformed=self.mode_changed)
        option_menu.setSelected(immediate_data['mode'] == 'on')
        # self.set_icon(menu_send)
        send_menu.setEnabled(racer_alive
                             and not compuracer_communication_lock.locked())
        option_menu.setEnabled(racer_alive
                               and not compuracer_communication_lock.locked())
        return [send_menu, option_menu]

    def start_request_transmitter(self, event):
        t = threading.Thread(name='Request transmitter',
                             target=self.send_requests_batched_to_racer,
                             args=(self.messages, ))
        t.start()

    @staticmethod
    def mode_changed(event):
        global immediate_data, compuracer_communication_lock
        is_selected = MenuFactory.button_selected(event)
        if is_selected != (immediate_data['mode'] == 'on'):
            with compuracer_communication_lock:
                if is_selected:
                    new_mode = 'on'
                else:
                    new_mode = 'off'
                if MenuFactory.set_immediate_mode_settings({'mode': new_mode}):
                    immediate_data['mode'] = new_mode
                    print("> Immediate mode enabled = {}".format(is_selected))
                else:
                    print("> Failed to enable immediate mode!")

    @staticmethod
    def set_same_result_messages(message, add_newline=True):
        global _textEditors
        if add_newline:
            message = "\n" + message
        for _textEditor in _textEditors:
            _textEditor.setText(str.encode(message))

    @staticmethod
    def set_result_messages(messages, add_newline=True):
        global _textEditors
        addition = ""
        if add_newline:
            addition = "\n"
        for i, _textEditor in enumerate(_textEditors):
            _textEditor.setText(str.encode(addition + messages[i]))

    @staticmethod
    def set_state_of_all_buttons(enabled):
        immediate_data_ui_elements["parallel_requests"].setEnabled(enabled)
        immediate_data_ui_elements["allow_redirects"].setEnabled(enabled)
        immediate_data_ui_elements["sync_last_byte"].setEnabled(enabled)
        immediate_data_ui_elements["send_timeout"].setEnabled(enabled)
        immediate_data_ui_elements["resend_batch"].setEnabled(enabled)

    @staticmethod
    def reset_request_tabs(the_requests):
        global _requestViewers, _requestPane
        _requestPane.removeAll()
        for i, request in enumerate(the_requests):
            _requestViewers.append(
                Cb.callbacks.createMessageEditor(None, False))
            _requestPane.addTab("Request {}".format(i),
                                _requestViewers[-1].getComponent())
            _requestViewers[-1].setMessage(request.getRequest(), True)

    @staticmethod
    def start_request_transmitter_button(event):
        t = threading.Thread(
            name='Request transmitter',
            target=MenuFactory.send_stored_requests_batched_to_racer,
            args=(event, ))
        t.start()

    @staticmethod
    def send_stored_requests_batched_to_racer(event):
        global _storedRequests
        if _storedRequests is not None and _storedRequests:
            MenuFactory.send_requests_batched_to_racer(_storedRequests, True)

    @staticmethod
    def send_requests_batched_to_racer(the_requests, resend=False):
        global _requestViewers, _textEditors, _storedRequests, \
            ADDITIONAL_SEND_TIMEOUT_WAIT, compuracer_communication_lock

        print("1")
        MenuFactory.set_state_of_all_buttons(False)
        if not resend:
            try:
                MenuFactory.reset_request_tabs(the_requests)
                _storedRequests = the_requests
            except Exception as e:
                print(e)
        print("2")
        with compuracer_communication_lock:
            MenuFactory.set_same_result_messages(
                "> Sending request(s) to CompuRacer..")
            for i in range(0, len(the_requests), 50):
                end = min(i + 50, len(the_requests))
                MenuFactory.send_requests_to_racer(the_requests[i:end])
            print("> Done sending {} request(s) to racer.".format(
                len(the_requests)))
            if immediate_data['mode'] == 'on':
                time.sleep(3)
                print("> Fetching results..")
                MenuFactory.set_same_result_messages(
                    "> Fetching result(s) from CompuRacer.. (takes up to {} seconds)"
                    .format(immediate_data['settings'][4] +
                            ADDITIONAL_SEND_TIMEOUT_WAIT))
                got_results = False
                # wait the send timeout + ADDITIONAL_SEND_TIMEOUT_WAIT seconds
                end_time = time.time() + immediate_data['settings'][
                    4] + ADDITIONAL_SEND_TIMEOUT_WAIT
                try:
                    while not got_results and time.time() < end_time:
                        success, results = MenuFactory.get_immediate_mode_results(
                        )
                        if success and results is not None and 'No results' not in results[
                                0]:
                            # set summary, full results and config
                            MenuFactory.set_result_messages(results)
                            print("4")
                            got_results = True

                        time.sleep(1)
                except Exception as e:
                    print(e)
                if not got_results:
                    MenuFactory.set_same_result_messages(
                        "> No results due to a timeout."
                        "\n> Please increase the send timeout and try again.")

            else:
                MenuFactory.set_same_result_messages(
                    "> The request is not send, so no results can be shown.\n"
                    "> Enable the immediate mode and send it again.")
        MenuFactory.set_state_of_all_buttons(True)

    # does not work..
    def load_icon(self):
        with open("icon_arrow--green.jpg", "rb") as in_file:
            pic = in_file.read()
        print(pic)
        try:
            self.icon = ImageIcon(pic, "CompuRacerIcon")
        except Exception as e:
            print(e)
            print("Cannot load image!")
        print(self.icon)
        print(self.icon.getImageLoadStatus())
        print(self.icon.getImageObserver())

    # does not work..
    def set_icon(self, menu_send):
        try:
            menu_send.setIcon(self.icon)
        except Exception as e:
            print("Failed! {}".format(e))

    @staticmethod
    def button_selected(event):
        button = event.getSource()
        return bool(button.getModel().isSelected())

    @staticmethod
    def item_selected(event):
        button = event.getSource()
        return int(str(button.getSelectedItem()))

    @staticmethod
    def get_immediate_mode_results():
        success = False
        results = None
        try:
            response = requests.get(url="http://{}:{}/{}".format(
                compuRacer_ip, compuRacer_port, immediate_results_path),
                                    timeout=5)
        except Exception as e:
            print("Oh noo:")
            print(e)
        else:
            # print("> Got response: {}\n".format(response.status_code))
            if int(response.status_code) < 300:
                if 'results' in response.json():
                    if response.json()[u'results'] is not None:
                        results = []
                        for item in response.json()[u'results']:
                            results.append(item.encode('ascii'))
                    else:
                        results = None
                    success = True
            else:
                print("> Failed to get immediate results!\n")
        return success, results

    @staticmethod
    def get_immediate_mode_settings():
        success = False
        mode = None
        settings = None
        try:
            response = requests.get(url="http://{}:{}/{}".format(
                compuRacer_ip, compuRacer_port, immediate_data_path),
                                    timeout=5)
        except Exception as e:
            print("Oh noo:")
            print(e)
        else:
            # print("> Got response: {}\n".format(response.status_code))
            if int(response.status_code) < 300:
                mode = response.json()[u'mode']
                settings = response.json()[u'settings']
                success = True
            else:
                print("> Failed to get immediate data!\n")
        return success, mode, settings

    @staticmethod
    def set_immediate_mode_settings(immediate_data):
        print("> Setting immediate data: {}..".format(immediate_data))
        success = False
        try:
            response_str = requests.post(url="http://{}:{}/{}".format(
                compuRacer_ip, compuRacer_port, immediate_data_path),
                                         json=immediate_data,
                                         timeout=5).status_code
        except Exception as e:
            print("Oh noo:")
            print(e)
        else:
            print("> Got response: {}\n".format(response_str))
            if int(response_str) < 300:
                print("> Success in settings immediate data.")
                success = True
            else:
                print("> Failed to set immediate data")
        return success

    @staticmethod
    def send_requests_to_racer(the_requests):
        print("> Sending {} request(s) to racer..".format(len(the_requests)))
        # header for request to racer
        global compuRacer_ip, compuRacer_port, add_request_path
        request_headers = [
            "POST /{} HTTP/1.1".format(add_request_path),
            "Host: {}:{}".format(compuRacer_ip, compuRacer_port),
            "Accept: */*",
            "Connection: close",
            "Content-Type: application/json",
        ]  # it auto-generates the content-length header
        print(request_headers)
        # build header and json body of sub-request
        total_body = {'requests': []}
        for i, request in enumerate(the_requests):
            details = Cb.helpers.analyzeRequest(request)
            headers_list = list(details.getHeaders())
            try:
                protocol = str(details.getUrl()).split(":")[0]
                url_end = headers_list[0].split(" ")[1]
                url_start = headers_list[1].split(": ")[1]
                print("Whole URL:", protocol, url_end, url_start)
                url = protocol + "://" + url_start + url_end
            except Exception as e:
                print("Header parsing failed! Skipping: {}".format(
                    details.getHeaders()))
                continue
            headers_list = headers_list[1:]
            try:
                headers = {
                    str(header[0]): str(header[1])
                    for header in
                    [header.split(": ") for header in headers_list]
                }
            except IndexError:
                print(
                    "Header parsing failed! Skipping: {}".format(headers_list))
                continue
            req_bytes = request.getRequest()
            body = ""
            if req_bytes and 0 < details.getBodyOffset() < len(req_bytes):
                try:
                    if 'Content-Type' in headers and headers[
                            'Content-Type'].startswith("multipart/form-data"):
                        body = "BASE64=" + base64.b64encode(
                            req_bytes[details.getBodyOffset():])
                    else:
                        body = Cb.helpers.bytesToString(
                            req_bytes[details.getBodyOffset():])
                except Exception as e:
                    print("Error:", e)
            else:
                body = ""
            total_body['requests'].append(
                json.dumps({
                    "url": str(url),
                    "method": str(details.getMethod()),
                    "headers": headers,
                    "body": body
                }))

        total_body_bytes = Cb.helpers.stringToBytes(json.dumps(total_body))
        print('Requests: ', len(total_body['requests']), ' body: ',
              len(total_body_bytes), 'bytes')

        print("> Sending requests: \n{}\n".format(request_headers[0]))
        try:
            response_str = requests.post(url="http://{}:{}/{}".format(
                compuRacer_ip, compuRacer_port, add_request_path),
                                         json=total_body,
                                         timeout=5).status_code
        except Exception as e:
            print("Oh noo:")
            print(e)
        else:
            print("> Got response: \n{}\n".format(response_str))
            if int(response_str) >= 300:
                print("> Failed to send the_requests: {}".format(total_body))
            else:
                print("> Done sending the_requests to racer!\n")

    @staticmethod
    def make_request(method, url, headers, body, timeout):
        try:
            response = requests.request(method=method,
                                        url="/".join(url.split("/")[1:]),
                                        headers=headers,
                                        body=body,
                                        timeout=timeout)
        except Exception as e:
            print(e)
            return 400
        else:
            return response.status_code
class MenuFactory(IContextMenuFactory):
    def __init__(self):
        self.invoker = None
        self.messages = None
        self.send_lock = threading.Lock()
        # self.load_icon()

    def createMenuItems(self, invoker):

        self.invoker = invoker

        if not (invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST
                or invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST
                or invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TABLE
                or invoker.getInvocationContext()
                == IContextMenuInvocation.CONTEXT_PROXY_HISTORY):
            return None

        self.messages = self.invoker.getSelectedMessages()
        if self.messages is None:
            return None
        if len(self.messages) > 1:
            button_text = "Send {} requests to CompuRacer".format(
                len(self.messages))
        else:
            button_text = "Send request to CompuRacer"
        global racer_alive
        if not racer_alive:
            button_text += " (offline)"
        elif self.send_lock.locked():
            button_text += " (busy)"
        menu_send = JMenuItem(button_text,
                              actionPerformed=self.start_request_transmitter)
        # self.set_icon(menu_send)
        menu_send.setEnabled(racer_alive and not self.send_lock.locked())
        return [menu_send]

    def start_request_transmitter(self, event):
        t = threading.Thread(name='Request transmitter',
                             target=self.send_requests_batched_to_racer,
                             args=(self.messages, ))
        t.start()

    def send_requests_batched_to_racer(self, requests):
        with self.send_lock:
            for i in range(0, len(requests), 50):
                end = min(i + 50, len(requests))
                self.send_requests_to_racer(requests[i:end])
            print("> Done sending {} request(s) to racer.".format(
                len(requests)))

    # does not work..
    def load_icon(self):
        with open("icon_arrow--green.jpg", "rb") as in_file:
            pic = in_file.read()
        print(pic)
        try:
            self.icon = ImageIcon(pic, "CompuRacerIcon")
        except Exception as e:
            print(e)
            print("Cannot load image!")
        print(self.icon)
        print(self.icon.getImageLoadStatus())
        print(self.icon.getImageObserver())

    # does not work..
    def set_icon(self, menu_send):
        try:
            menu_send.setIcon(self.icon)
        except Exception as e:
            print("Failed! {}".format(e))

    @staticmethod
    def send_requests_to_racer(requests):
        print("> Sending {} request(s) to racer..".format(len(requests)))
        # header for request to racer
        global compuRacer_ip, compuRacer_port, add_request_path
        request_headers = [
            "POST /{} HTTP/1.1".format(add_request_path),
            "Host: {}:{}".format(compuRacer_ip, compuRacer_port),
            "Accept: */*",
            "Connection: close",
            "Content-Type: application/json",
            #"X-Requested-With: Burp extension"
        ]  # it auto-generates the content-length header
        print(request_headers)
        # build header and json body of sub-request
        total_body = {'requests': []}
        for i, request in enumerate(requests):
            details = Cb.helpers.analyzeRequest(request)
            headers_list = list(details.getHeaders())
            try:
                protocol = str(details.getUrl()).split(":")[0]
                url_end = headers_list[0].split(" ")[1]
                url_start = headers_list[1].split(": ")[1]
                print("Whole URL:", protocol, url_end, url_start)
                url = protocol + "://" + url_start + url_end
            except Exception as e:
                print("Header parsing failed! Skipping: {}".format(
                    details.getHeaders()))
                continue
            headers_list = headers_list[1:]
            try:
                headers = {
                    str(header[0]): str(header[1])
                    for header in
                    [header.split(": ") for header in headers_list]
                }
            except IndexError:
                print(
                    "Header parsing failed! Skipping: {}".format(headers_list))
                continue
            req_bytes = request.getRequest()
            body = ""
            if req_bytes and 0 < details.getBodyOffset() < len(req_bytes):
                try:
                    if 'Content-Type' in headers and headers[
                            'Content-Type'].startswith("multipart/form-data"):
                        body = "BASE64=" + base64.b64encode(
                            req_bytes[details.getBodyOffset():])
                    else:
                        body = Cb.helpers.bytesToString(
                            req_bytes[details.getBodyOffset():])
                except Exception as e:
                    print("Error:", e)
            else:
                body = ""
            total_body['requests'].append(
                json.dumps({
                    "url": str(url),
                    "method": str(details.getMethod()),
                    "headers": headers,
                    "body": body
                }))

        total_body_bytes = Cb.helpers.stringToBytes(json.dumps(total_body))
        print('Requests: ', len(total_body['requests']), ' body: ',
              len(total_body_bytes), 'bytes')

        request = Cb.helpers.buildHttpMessage(request_headers,
                                              total_body_bytes)
        print("> Sending requests: \n{}\n".format(request_headers[0]))
        try:
            response = Cb.callbacks.makeHttpRequest(
                Cb.helpers.buildHttpService(compuRacer_ip,
                                            int(compuRacer_port), False),
                request)
        except Exception as e:
            print("Oh noo:")
            print(e)
            return
        response_str = Cb.helpers.bytesToString(response.getResponse())
        print("> Got response: \n{}\n".format(response_str))
        if not 200 <= Cb.helpers.analyzeResponse(
                response.getResponse()).getStatusCode() <= 299:
            print("> Failed to send requests: {}".format(total_body))
        else:
            print("> Done sending requests to racer!\n")