// This file written mostly by Tim Johnson (Tim@TimMUD)
#include <save.h>

static void process_channel(mixed fd, mixed *info){
    string mudname;
    string sendermsg, sendername, senderrealname, sendermud;
    string targetname, targetmud, targstr = "";
    //trr("extra stuff is ["+info[0][8..]+"]");
    switch(info[0][8..]){ // what is after the "channel-"
    case "m": // message
    case "e": // emote
    case "t": // targetted emote
        //trr("they want to do a message...");
        // (drop-through from m/e is intentional)
        // Probably should check if the target is 0@0 like it should be,
        // and give a warning if it's not... I don't carethough, I'll just
        // ignore the target and save some CPU.
        // Check if string parts are strings...
        if(info[0][8..]=="t"){
            if(sizeof(info)!=13 || !stringp(info[9]) || !stringp(info[10]) ||
              !stringp(info[11]) || !stringp(info[12])){
                send_error(info[2],info[3],"bad-pkt","Bad packet format.",info);
                return;
            }
            if(!grepp(info[9],"$N")){
                info[9] = info[9] + " (from "+info[11]+"@"+info[2]+")";
                info[10] = info[10] + " (from "+info[11]+"@"+info[2]+")";
            }
            sendername = info[11];
            senderrealname = info[3];
            sendermud = info[2];
            targetname = info[12];
            targetmud = info[7];
            sendermsg = info[9];
        }
        else{ // m, e
            if(sizeof(info)!=9){
                send_error(info[2],info[3],"bad-pkt","Bad packet format.",info);
                return;
            }
            if(info[0] == "channel-e" && !grepp(info[8],"$N"))
                info[8] = info[8] + " (from "+info[7]+"@"+info[2]+")";
            sendername = info[7];
            senderrealname = info[3];
            sendermud = info[2];
            sendermsg = info[8];
        }
        if(targetname && targetmud) {
            targstr = "Target: "+targetname+"@"+targetmud;
        }

        if(info[4]!=0 || info[5]!=0 || !stringp(info[6]) ||
          !stringp(info[7]) || !stringp(info[8])){
            send_error(info[2],info[3],"bad-pkt","Bad packet format.",info);
            return;
        }
        if(!channels[info[6]]){ // error, channel is not registered!
            send_error(info[2],info[3],"unk-channel",
              "Unknown channel: "+info[6],info);
            return;
        }
        if(member_array(info[2],listening[info[6]])==-1){
            // if mud is not listening, then make them listen
            // and let them know they weren't listening.
            send_error(info[2],info[3],"warning",
              // No applicable message codes... change
              // "warning" to something else if you want.
              "Your mud wasn't listening to "+info[6]+
              ", so I'll start you listening right now.",info);
            // Could do all the listen stuff, or could just
            // pretend they requested it... I'm gonna pretend...
            process_channel(fd,({ "channel-listen", 5, info[2], 0,
                router_name, 0, info[6], 1 }));
            //return;
        }
        if(channels[info[6]][0]==2){ // filtered channel
            if(channels[info[6]][1]!=info[2]){ // not from chan owner
                // if from anybody else other than the owner,
                // check if the owner is online, then either give
                // an error, or forward to the owner...
                if(!connected_muds[channels[info[6]][1]]){
                    send_error(info[2],info[3],"unk-dst",
                      channels[info[6]][1]+" is not online to filter "+info[6],
                      info);
                    return;
                }
                if(info[4]!=0 || info[5]!=0 || !stringp(info[6]) ||
                  !stringp(info[7]) || !stringp(info[8])){
                    send_error(info[2],info[3],"bad-pkt","Bad packet format",info);
                    return;
                }
                write_data(connected_muds[channels[info[6]][1]],({
                    "chan-filter-req",
                    5,
                    router_name,
                    0,
                    channels[info[6]][1], // mud name of chan owner
                    0,
                    info[6],
                    info
                  }));
                return;
            }
            // IS from chan owner, just broadcast it...
            // drop through and broadcast like the other types do...
        }
        //trr("CHAN: I think it's a good message at this point...");
        // at this point, they're wanting to do a message on a
        // selective banned/allowed channel, or else are the owner
        // of a filtered channel and they have not been
        // blocked, so I will just do it...

        server_log(sendername+"("+senderrealname+")@"+sendermud+" "+": "+
          sendermsg+" "+targstr+"\n",info[6]);

        if(intp(fd)) this_object()->SendMessage(info);

        else if(member_array(fd, this_object()->GetBannedMuds()) != -1){
            //trr("router: banned mud "+fd+" tried to send a channel message");
            return;
        }

        info[8] = "/secure/daemon/filter"->eventFilter(info[8]);

        foreach(mudname in keys(connected_muds)){
            if(listening[info[6]] && member_array(mudname, listening[info[6]])!=-1)
                write_data(connected_muds[mudname],info);
        }

        //broadcast_data(muds_not_on_this_fd(fd), info);
        // send to all the other fd's that have a mud listening
        //			write_data(fd,info);
        // also send the message to the connection that this mud is on
        // from too... why do this? 'cause I'm allowing multiple muds to
        // log on from the same connection :)
        // I think the normal I3 server does this too, but I don't know
        // since I can't test at the moment.
        return;
    case "add":
        // check if already exists...
        if(!info[6] || (info[6] && !stringp(info[6]))) return;
        if(channels[info[6]]){
            //trr(info[3]+"@"+info[2]+" failed to create the channel: "+info[6]+ "because it already exists.","red");
            return;
        }
        // check if a valid channel name (illegal characters?)
        // check if other stuff is valid, like channel_type is 0,1,2
        // at this point, is being successfully added...
        channel_update_counter++;
        channels[info[6]]=({ info[7], info[2], ({}) });
        channel_updates[info[6]] = channel_update_counter;
        //trr(info[3]+"@"+info[2]+" created the channel: "+info[6],"yellow");
        server_log(info[3]+"@"+info[2]+" created the channel: "+info[6]+"\n");
        // broadcast an update saying that this channel is added or changed now
        // chanlist-reply packet to everybody (who has a channel service?)
        broadcast_chanlist(info[6]);
        SendList( ([ "channels" : ([ info[6] : channels[info[6]] ]), "listening" : ([]) ]),
        0, "chanlist" );
    //			broadcast_data(muds_not_on_this_fd(fd), ({
    //				"chanlist-reply",5,router_name,0,
    //				0, // should be mudname, I think 0 would work just as well?
    //				0, channel_update_counter,
    //				([ info[6]:({ info[2], info[7] }) ])
    //			}));
    //			write_data(fd,({
    //				"chanlist-reply",5,router_name,0,
    //				0, // should be mudname, I think 0 would work just as well?
    //				0, channel_update_counter,
    //				([ info[6]:({ info[2], info[7] }) ])
    //			}));
    save_object(SAVE_ROUTER);
    return;
case "remove":
    if(!channels[info[6]]){ // error, channel is not registered!
        send_error(info[2],info[3],"unk-channel",
          "Unknown channel: "+info[6],info);
        return;
    }
    //trr("test1: "+clean_fd(socket_address(fd)));
    //trr("test2: "+router_ip);
    //trr("test3: "+channels[info[6]][1]);
    //trr("test4: "+info[2]);

    if(channels[info[6]][1]!=info[2] && 
      info[2] != mud_name() &&
      clean_fd(socket_address(fd)) != router_ip ){
        send_error(info[2],info[3],"not-allowed","Channel "+
          info[6]+" owned by: "+channels[info[6]][1],info);
        return;
    }
    // at this point, is being removed by the owner...
    channel_update_counter++;
    map_delete(channels,info[6]);
    map_delete(channel_updates,info[6]);
    //channel_updates[info[6]] = channel_update_counter;
    //trr(info[3]+"@"+info[2]+" deleted the channel: "+info[6],"yellow");
    server_log(info[3]+"@"+info[2]+" deleted the channel: "+info[6]+"\n");
    // broadcast an update saying that this channel is gone now
    broadcast_chanlist(info[6]);
    save_object(SAVE_ROUTER);
    SendList( ([ "channels" : ([ info[6] : -1 ]), "listening" : ([]) ]),
      0, "chanlist" );
    return;
case "admin":
    // add/delete muds from the 2 lists...
    //trr("test1: "+clean_fd(socket_address(fd)));
    //trr("test2: "+router_ip);
    //trr("test3: "+channels[info[6]][1]);
    //trr("test4: "+info[2]);
    if(channels[info[6]][1]!=info[2] && 
      clean_fd(socket_address(fd)) != router_ip ){
        send_error(info[2],info[3],"not-allowed","Channel "+
          info[6]+" owned by: "+channels[info[6]][1],info);
        return;
    }
    if(!listening[info[6]]) listening[info[6]] = ({});
    if(sizeof(info[7])){ // add to list...
        //trr("planning to add.","white");
        channels[info[6]][2] += info[7];
        // if add to ban list, unlisten...
        if(channels[info[6]][0]==0){ // type 0 means selective ban
            //trr(identify(info[7]) +" has been banned from "+info[6],"yellow");
            listening[info[6]] -= info[7];
        }
        //else //trr(identify(info[7]) +" has been unbanned from "+info[6],"yellow");
    }
    if(sizeof(info[8])){ // remove from list...
        //trr("channels[info[6]]: "+identify(channels[info[6]]),"white");
        //trr("planning to remove","white");
        //trr("info: "+identify(info),"white");
        channels[info[6]][2] -= info[8];
        //trr("channels[info[6]]: "+identify(channels[info[6]]), "white");
        if(channels[info[6]][0]!=0){ // type 0 means selective ban...
            // selective allow and filtered are the same though...
            // so if not selective ban, then act like selective allow...
            // if removed from allow list, unlisten...
            listening[info[6]] -= info[8];
            //trr(identify(info[8])+" has been banned from "+info[6],"yellow");
        }
        //else //trr(identify(info[8])+" has been unbanned from "+info[6],"yellow");
        //else listening[info[6]] += info[8];
    }
    //trr("Channel data for "+info[6]+": "+identify(channels[info[6]]), "white");
    SendList( ([ "channels" : ([ info[6] : channels[info[6]] ]),
      "listening" : ([ info[6] : listening[info[6]] ]) ]),
0, "chanlist" );
save_object(SAVE_ROUTER);
return;
case "listen": // mudname=info[2], channame=info[6], on_or_off=info[7]
if(!channels[info[6]]){ // error, channel is not registered!
    send_error(info[2],info[3],"unk-channel",
      "Unknown channel: "+info[6],info);
    return;
}
if(!listening) listening=([ ]);
if(!listening[info[6]]) listening[info[6]]=({ }); // first MUD to listen
if(info[7]){ // on_or_off is on
    if(member_array(info[2],listening[info[6]])!=-1)
        return; // already listening, ignore them
}
else{ // on_or_off is off
    if(member_array(info[2],listening[info[6]])==-1)
        return; // already NOT listening, ignore them
}
//trr("listening change on chan:"+info[6]+", mud="+info[2]+", on_or_off="+info[7]);
// only CHANGES should get to this point
switch(channels[info[6]][0]){
case 0: // selectively banned
    if(member_array(info[2],channels[info[6]][2])!=-1){
        // in list, you're banned...
        send_error(info[2],0,"not-allowed",
          "Banned from "+info[6],info);
        save_object(SAVE_ROUTER);
        return;
    }
    // not in ban list at this point
    if(info[7])
        listening[info[6]] += ({ info[2] });
    else
        listening[info[6]] -= ({ info[2] });
    save_object(SAVE_ROUTER);
    SendList( ([ "channels" : ([]), "listening" : ([ info[6] : listening[info[6]] ]) ]),
    0, "chanlist" );
return;
case 1: // selectively allowed
if(member_array(info[2],channels[info[6]][2])==-1 &&
  info[2] != channels[info[6]][1]){
    // NOT in list, you're banned!
    send_error(info[2],0,"not-allowed",
      "Not in allow list for "+info[6],info);
    return;
}
// in allow list at this point
if(info[7])
    listening[info[6]] += ({ info[2] });
else
    listening[info[6]] -= ({ info[2] });
save_object(SAVE_ROUTER);
SendList( ([ "channels" : ([]), "listening" : ([ info[6] : listening[info[6]] ]) ]),
0, "chanlist" );
return;
case 2: // filtered... act like selectively allowed
if(member_array(info[2],channels[info[6]][2])==-1 &&
  info[2] != channels[info[6]][1]){
    // NOT in list, you're banned!
    send_error(info[2],0,"not-allowed",
      "Not in allow list for "+info[6],
      info);
    return;
}
// in allow list at this point
if(info[7])
    listening[info[6]] += ({ info[2] });
else
    listening[info[6]] -= ({ info[2] });
save_object(SAVE_ROUTER);
SendList( ([ "channels" : ([]), "listening" : ([ info[6] : listening[info[6]] ]) ]),
0, "chanlist" );
return;
} // switch
default: // trying to do "channel-blah"
send_error(info[2],info[3],"unk-type","I don't know what "+info[0]+
  " means.",info);
//trr("Don't know what the ["+info[0]+"] packet means.", "yellow");
return;
}
//trr("can't get here?");
}
