//function used to communicate with the sequencer so as to join the chat group.
int chatClient::dojoin(string rs_ip, int rs_port){
    string outmsg;
    int outlen, saddr_len;
    s_ip = rs_ip;
    s_port = rs_port;
    //setupt the UDP socket
    UDP joinUDP;
    joinUDP.setRemoteAddr(s_ip.c_str(),s_port);
    
    //args: sequencer's ip, port, myIP, myPort,myName;
    myMsg message = mmaker.makeJoin(name);
    msgMaker::serialize(outmsg, outlen, message);    
    
    
    
    
    if(joinUDP.sendToNACK(outmsg.c_str(),outlen)==-2){
        cerr<<"Error! Not able to join to the group... App is about to exit..."
            <<endl;
        exit(-1);
    }
    
    
    return 1;
}
int chatClient::dojoin(string &rs_ip, int &rs_port, UDP &listener){
    string outmsg;
    int outlen, saddr_len, recvlen, MAX_MSG_LEN=2048;
    char recvMsg[2048];
    const string myName,myIP;
    int myPort,myID;
    s_ip = rs_ip;
    s_port = rs_port;
    
    //setupt the UDP socket
    UDP joinUDP;
    joinUDP.setRemoteAddr(s_ip.c_str(),s_port);
    
    //args: sequencer's ip, port, myIP, myPort,myName;
    myMsg message = mmaker.makeJoin(name);
    msgMaker::serialize(outmsg, outlen, message);    
    if(joinUDP.sendToNACK(outmsg.c_str(),outlen)==-2){
        cerr<<"Error! Not able to join to the group... App is about to exit..."
            <<endl;
        exit(-1);
    }
   
    while(1){
        recvlen = listener.recvFromNACK(recvMsg, MAX_MSG_LEN, myName, myIP, 
                                        myPort, myID);
        string reMsg(recvMsg,recvlen);
        msgParser tempParser(reMsg.c_str(),recvlen);
        processMSG(reMsg.c_str(),recvlen); 
        if(tempParser.msgTypeIs()==join_ack){
            rs_ip = s_ip;
            rs_port = s_port;
            break;
        }
        
    }
    
    
    
    
    return 1;
}
//fuction actually used to process the message.
//it will analyze the header of the message and take actions accordingly.
int chatClient::processMSG(const char* msg, int mlen)
{
    peer newUser;
    string newName, newIP, textmsg, outmsg;
    int newPort, newID;
    int outlen,i;
    int seqNum =0;
    myMsg tempMsg;
    vector<peer>::iterator aIter;
    msgParser parser(msg, mlen);
    
        
    if(parser.isACK()){
        //return 0;
        //since all the UDP_ACKs are handled in the underlaying UDP-wrapper layer, we simply
        //ignore the this message.
    }
    else{
        switch (parser.msgTypeIs()) {
            case join:
                if(status!=ELEC){
                    status=NORMAL;
                    //setup and send a Navi message
                    mmaker.setInfo(sname,s_ip, s_port,s_id);
                    tempMsg = mmaker.makeNavi();
                    msgMaker::serialize(outmsg,outlen,tempMsg);
                    parser.senderInfo(newIP,newName,newPort,newID);
                    
                    UDP joinUDP;
                    joinUDP.setRemoteAddr(newIP.c_str(),newPort);
                    joinUDP.sendToNACK(outmsg.c_str(),outlen);
                    
                    mmaker.setInfo(name,IP, port,C_ID);
                    return 1;
                }
                else{
                    //receive a Join request message during the election, 
                    //push it into message queue and
                    //handle it later on.
                    inMsgQ.push(msg);
                    return 1;
                }
                break;
            case navi:
                //the chatClient get a navi message, which means the one 
                //he asked is not the sequencer, and the info of the 
                //sequencer is returned via this message.
                //get the info of the sequencer and send another join 
                //message to it.
                
                parser.senderInfo(newIP, newName, newPort,newID);
                dojoin(newIP, newPort);
                return 1;
                break;
            case join_ack:
                //get the peerlist and client_id decided by the sequencer 
                //and store them locally for future use.
                
                parser.joinFeedback(msgMaxCnt, C_ID, clientList);
                parser.senderInfo(newIP,newName,newPort,newID);
                s_id = newID;
                s_ip = newIP;
                s_port = newPort;
                sname = newName;
                clntUDP.setRemoteAddr(s_ip.c_str(), s_port);
                //call the setInfo again so as to set the C_ID field
                mmaker.setInfo(name,IP, port,C_ID);
                displayClients();
                status = NORMAL;
                return 1;
                break;
                
            case join_broadcast:
                //get the ip ,port name and client ID of the new user 
                //and store them locally.
                
                if(status!=ELEC){
                    status=NORMAL;
                    parser.senderInfo(newIP, newName, newPort, newID);
                    if(newID==C_ID){
                        return 1;
                    }
                    parser.joinName(newName);
                    memset(&newUser, 0, sizeof(peer));
                    memcpy(newUser.name,newName.c_str(),newName.size());
                    memcpy(newUser.ip, newIP.c_str(), newIP.size());
                    newUser.c_id = newID;
                    newUser.port = newPort;
                    clientList.push_back(newUser);
                    cout<<"NOTICE "<<newUser.name<<" joined on "
                        <<newUser.ip<<":"<<newUser.port<<endl;
                    return 1;
                }
                else{
                    //in wrong state, push the message into message queue 
                    //and handle later on
                    cerr<<"Unexpected Join Broadcast message @"<<name
                        <<" !"<<endl;
                    string temps(msg,mlen);
                    inMsgQ.push(temps);
                    return -1;
                }
                break;
            case leave_broadcast:{
                //remove the specific user from the peer list
                vector<peer> timeoutList;
                if(status!=ELEC){
                    status=NORMAL;
                    parser.senderInfo(newIP, newName, newPort,newID);
                    
                    for(aIter = clientList.begin(); 
                        aIter != clientList.end(); aIter++){
                        if((*aIter).c_id==newID){
                            cout<<"NOTICE "<<aIter->name
                                <<" left the chat or crashed"<<endl;
                            clientList.erase(aIter);
                            break;
                        }
                    }
                    //sequencer leaves
                    if (newID==s_id) {
                        for(aIter = clientList.begin(); 
                            aIter != clientList.end(); aIter++){
                            if((*aIter).c_id==newID){
                                clientList.erase(aIter);
                                cout<<"NOTICE "<<aIter->name
                                    <<" left the chat or crashed"<<endl;
                                break;
                            }
                        }
                        if(doElection()==1){
                            
                            return 10;
                        }
                        else return -10;
                    }
                    
                }
            
                else{
                    cerr<<"Unexpected Leave Broadcast message @"<<name
                        <<" !"<<endl;
                    string temps(msg,mlen);
                    inMsgQ.push(temps);
                    return -1;
                }
                break;
            }
            case msg_broadcast:
                
                //show the message to the standard output
                if(status!=ELEC){
                    status =NORMAL;
                    if(parser.getMsg(seqNum,textmsg)<0){
                        cerr<<"failed to get the message content! @"
                            <<name<<endl;
                        exit(-1);
                    }
                    
                    //normal case
                    if(seqNum==(msgMaxCnt+1)){
                            cout<<textmsg<<endl;
                            msgMaxCnt++;
                        }
                    //receive duplicated message, ignore.
                    else if(seqNum<(msgMaxCnt+1)){
                    //ignore;
                    }
                    //missed earlier messages, search & wait for retransimit.
                    else if(seqNum>(msgMaxCnt+1)){
                            dspMsg.insert(pair<int,string>(seqNum,textmsg));	
                            it = dspMsg.find((msgMaxCnt+1));			
                            while(it!=dspMsg.end()){
                                    cout<<(*it).second<<endl;
                                    dspMsg.erase(it);
                                    msgMaxCnt++;
                                    it = dspMsg.find((msgMaxCnt+1));
                            }	
                    }		    
                    return 1;
                }
                else{
                    cerr<<"Unexpected msg Broadcast message @"<<name
                        <<" !"<<endl;
                    string temps(msg,mlen);
                    inMsgQ.push(temps);
                    return -1;
                }
                break;
            case election_req:{
                //do BULLY
                if(status==NORMAL){
                    parser.senderInfo(newIP, newName, newPort,newID);
                    
                    if(C_ID>=newID){
                        if(doElection()>0){
                            
                            return 10;
                        }
                        else return -10;
                    }
                }
                else return -1;
                break;
            }
            case election_ok:
                //do BULLY
                if(status!=ELEC){
                    cerr<<"Unexpected msg election ok message @"<<name
                        <<" !"<<endl;
                    exit(-1);
                }
                else{
                    electWin=false;
                }
                return 1;
                break;
            case leader_broadcast:
                //get and setup the info of the new sequencer
                //here we just ignore the state which this client is in. 
                //Cause it's safe 
                //to change the sequencer here.
                if(status==ELEC_CLIENT||status==NORMAL){
                    status = NORMAL;
                    parser.senderInfo(newIP, newName, newPort,newID);
                    if(newID!=C_ID&&newID!=s_id){
                        
                        s_ip = newIP;
                        s_port = newPort;
                        s_id = newID;
                        sname = newName;
                        pthread_mutex_lock(&udpMutex);
                        clntUDP.updateSocket(s_ip.c_str(),s_port);
                        pthread_mutex_unlock(&udpMutex);
                        return 9;
                    }
                }return 1;
                break;
            default: //not used here.
                break;
        }
    }
}