# -*- coding: utf-8 -*-

# +---------------------------------------------------------------------------+
# |          01001110 01100101 01110100 01111010 01101111 01100010            |
# |                                                                           |
# |               Netzob : Inferring communication protocols                  |
# +---------------------------------------------------------------------------+
# | Copyright (C) 2011-2014 Georges Bossert and Frédéric Guihéry              |
# | This program is free software: you can redistribute it and/or modify      |
# | it under the terms of the GNU General Public License as published by      |
# | the Free Software Foundation, either version 3 of the License, or         |
# | (at your option) any later version.                                       |
# |                                                                           |
# | This program is distributed in the hope that it will be useful,           |
# | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              |
# | GNU General Public License for more details.                              |
# |                                                                           |
# | You should have received a copy of the GNU General Public License         |
# | along with this program. If not, see <http://www.gnu.org/licenses/>.      |
# +---------------------------------------------------------------------------+
# | @url      : http://www.netzob.org                                         |
# | @contact  : contact@netzob.org                                            |
# | @sponsors : Amossys, http://www.amossys.fr                                |
# |             Supélec, http://www.rennes.supelec.fr/ren/rd/cidre/           |
# +---------------------------------------------------------------------------+

# +---------------------------------------------------------------------------+
# | File contributors :                                                       |
# |       - Georges Bossert <gbossert (a) miskin.fr>                          |
# +---------------------------------------------------------------------------+

# +---------------------------------------------------------------------------+
# | Standard library imports                                                  |
# +---------------------------------------------------------------------------+
from threading import Thread
import time

# +---------------------------------------------------------------------------+
# | Related third party imports                                               |
# +---------------------------------------------------------------------------+
from pylstar.LSTAR import LSTAR

# +---------------------------------------------------------------------------+
# | Local application imports                                                 |
# +---------------------------------------------------------------------------+
from netzob.Common.Utils.Decorators import NetzobLogger
from netzob.Common.Models.Simulator.AbstractionLayer import AbstractionLayer
from netzob.Inference.Grammar.GenericMAT import GenericMAT



@NetzobLogger
class ActiveGrammaticalInference(Thread):
    """This class can be used to actively infer the state machine of the targeted protocol.

    >>> from netzob.all import *
    >>> import time
    >>> #
    >>> # Create a fake automata to infer
    >>> #
    >>> # the input vocabulary
    >>> s_a = Symbol(name="a", fields=[Field("a\\n")])
    >>> s_b = Symbol(name="b", fields=[Field("b\\n")])
    >>> s_c = Symbol(name="c", fields=[Field("c\\n")])
    >>> input_symbols = [s_a, s_b, s_c]
    >>> # the output vocabulary
    >>> s_1 = Symbol(name="1", fields=[Field("1\\n")])
    >>> s_2 = Symbol(name="2", fields=[Field("2\\n")])
    >>> s_3 = Symbol(name="3", fields=[Field("3\\n")])
    >>> output_symbols = [s_1, s_2, s_3]
    >>> # the states
    >>> q0 = State(name="q0")
    >>> q1 = State(name="q1")
    >>> q2 = State(name="q2")
    >>> # the transitions
    >>> t0 = Transition(q0, q0, s_a, [s_1])
    >>> t1 = Transition(q0, q0, s_b, [s_1])
    >>> t2 = Transition(q0, q1, s_c, [s_3])
    >>> t3 = Transition(q1, q0, s_a, [s_1])
    >>> t4 = Transition(q1, q1, s_b, [s_2])
    >>> t5 = Transition(q1, q2, s_c, [s_3])
    >>> t6 = Transition(q2, q0, s_a, [s_1])
    >>> t7 = Transition(q2, q2, s_b, [s_3])
    >>> t8 = Transition(q2, q1, s_c, [s_2])
    >>> # creation of the fake automata
    >>> qstart = State(name="initial_state")
    >>> tstart = OpenChannelTransition(qstart, q0)
    >>> fake_automata = Automata(qstart, input_symbols)
    >>> # creation of an actor 
    >>> channel = TCPServer(localIP = "127.0.0.1", localPort=8888, timeout=5)
    >>> aLayer = AbstractionLayer(channel, input_symbols + output_symbols)
    >>> fake_actor = Actor(fake_automata, initiator = False, abstractionLayer = aLayer)
    >>> try:
    ...     # configure our inference process
    ...     inf_channel = TCPClient(remoteIP="127.0.0.1", remotePort=8888)
    ...     inference = ActiveGrammaticalInference(inf_channel, input_symbols, output_symbols, target_process=fake_actor)
    ...     inference.start()
    ...     inference.wait_is_done()
    ... finally:
    ...    fake_actor.stop()




    """


    def __init__(self, channel, input_symbols, output_symbols, max_states = 15, target_process=None):
        Thread.__init__(self)
        self.channel = channel
        self.input_symbols = input_symbols
        self.output_symbols = output_symbols
        self.max_states = max_states
        self.target_process = target_process

    def run(self):
        self._logger.info("Starting the grammatical inference process")

        aLayer = AbstractionLayer(self.channel, self.input_symbols + self.output_symbols)
        mat = GenericMAT(abstraction_layer = aLayer, target_process = self.target_process)

        lstar = LSTAR(self.input_symbols, mat, max_states = self.max_states)
        return lstar.learn()
                      
        
        
                

    def wait_is_done(self):
        """This method returns when the inference has finished"""

        self._logger.debug("Waiting for the end of the Active Grammatical Inference Process")
        self.join()            
        self._logger.debug("ActiveGrammaticalInference has finished.")
    
