import socket
import struct
import time
from pprint import pprint
import os 

# --- Placeholder for your z7 function ---
def z7(reqData):
    # This simulates your function converting data to a raw string
    return f"RAW_DATA[{reqData}]" 

# Configuration
HOST        = 'zshlo.com'
CHUNK_SIZE  = 8192
PORT        = 7856

print(f"Connected to {HOST} on port {PORT}. Type 'exit' to quit.")



reqDict ={

    'i_rec'    :  4, 
    'rm_rec'   :  5, 
    'r_rec'    :  6,
    'w_rec'    :  7,

    'r_block'  :  8,
    'w_block'  :  9,
    'r_file'   : 10,
    'w_file'   : 11,

    'a_data'   : 12,
    'a_ifile'  : 13,
    'r_ifile'  : 14,
    'w_ifile'  : 15,
}

header_format = '<iiiiIIBBH4sQQQQ192s'


def pack_header(user_input):
    tokens    = user_input.split(' ')
    token_0th = tokens[0] if len(tokens)>0 else ''
    token_1th = tokens[1] if len(tokens)>1 else ''
    token_2th = tokens[2] if len(tokens)>2 else ''
    # Use tokens[1] if it exists, otherwise empty
    char_input = tokens[1] if len(tokens) > 1 else ""


    # 1. Prepare Data Components
    FID, RID, num, iblock = 0, 0, 0, 0
    
    # FIX: ext must be bytes to match '4s'
    #ext = b"txt " 
    ctime = int(time.time())
    mtime = int(time.time())
    

    print('51:', tokens )
    print('51:', token_0th )
    

    #   FID 
    #   FID [blockNum] num 

    #  get reqType r_file 
    if '/' in token_0th or '@' in token_0th :
        token_1th = token_0th        
        token_0th = 'r_file'
        char_input = token_1th

        
    #get FID 
    if token_1th.isdigit():
        FID = int(token_1th)

    #get blockNum 
    if token_2th.isdigit():
        #block num 
        num = int(token_2th)
        print('74:', num)


    #RID 
    if token_1th.startswith('-'):   RID = 16   #hide record 
    if token_1th.startswith('-'):   RID = 16   #hide record 
    #-----------------------------------------------
    #   RID 0
    #   RID 1 

    if  token_0th  == 'w_file': 
        print('53:', token_0th, token_2th )
        if os.path.isfile(token_2th):
            print('54:', token_2th)
            token_1th += token_2th[1:]
            char_input = token_1th
            num       = os.path.getsize(token_2th)

    elif token_0th  == 'a_data': 
        print('53:', token_0th, token_2th )


    print('98:', token_0th)
    print('98:', char_input)
    #if token_0th  == 'w_file': 
    #if token_0th  == 'w_file': 

    z_str       = token_1th
    basename    = os.path.basename(z_str)
    ext         = os.path.splitext(z_str)[1].lower()
    #if tokens_nth.startswith('z@'): ext = 'z@'
    if    ext                  : ext = ext
    elif '/t/' in z_str        : ext = 't/'  
    elif 'z@' in z_str         : ext = 'z@'
    elif z_str.startswith('u/'): ext = 'u/'
    elif z_str.startswith('s/'): ext = 's/'


    
    # FIX: Using .get() safely
    reqType = reqDict.get(token_0th, 0)
    keyClass = 0
    length = len(tokens[-1])
    k0, k1, k2, k3 = 0, 0, 0, 0
    
    # Prepare 192-byte character data
    ext       = ext.encode('utf-8').ljust(4, b'\0')[:4]
    char_data = char_input.encode('utf-8').ljust(192, b'\0')[:192]

    # 2. Format String
    # <      : Little-endian
    # iiii   : 4 signed ints (16 bytes)
    # II     : 2 unsigned ints (8 bytes)
    # BBH    : 1 byte, 1 byte, 2 bytes (4 bytes total)
    # 4s     : 4-byte string (4 bytes)
    # QQQQ   : 4 unsigned long longs (32 bytes)
    # 192s   : 192-byte string


    # 3. Pack the data
    try:
        header = struct.pack(
            header_format,
            FID     ,RID      ,num    ,iblock, # iiii
            ctime   ,mtime    ,                 # II
            reqType ,keyClass ,length  ,        # BBH
            ext,                                # 4s
            k0, k1, k2, k3,                     # QQQQ
            char_data                           # 192s
        )
        return header
    except struct.error as e:
        print(f"Packing error: {e}")
        return None

def bytes_to_dict(packed_bytes):
    # 1. Define the EXACT same format string used for packing
    # < : little-endian
    # iiii : 4 ints (16 bytes)
    # II   : 2 unsigned ints (8 bytes)
    # BBH  : 1 byte, 1 byte, 2 byte (4 bytes)
    # QQQQ : 4 unsigned long longs (32 bytes)
    # 192s : 192 bytes of string

    
    # 2. Unpack the bytes into a tuple
    unpacked_tuple = struct.unpack(header_format, packed_bytes)
    
    # 3. Map the tuple values to a dictionary
    result_dict = {       
        #   4byte 
        "FID"       : unpacked_tuple[0],
        "RID"       : unpacked_tuple[1],
        "num"       : unpacked_tuple[2],
        "iblock"    : unpacked_tuple[3],

        #   4byte 
        "ctime"     : unpacked_tuple[4],
        "mtime"     : unpacked_tuple[5],

        "reqType"   : unpacked_tuple[6],
        "keyClass"  : unpacked_tuple[7],
        "length"    : unpacked_tuple[8],
        "ext"       : unpacked_tuple[9],

        "k0"       :  unpacked_tuple[10],
        "k1"       :  unpacked_tuple[11],
        "k2"       :  unpacked_tuple[12],
        "k3"       :  unpacked_tuple[13],
        # Decode bytes back to string, stripping null padding
        "char_data": unpacked_tuple[14].decode('utf-8').rstrip('\x00')
    }
    #print('183: reqType', reqDict(,'unknown')

    reqType_value= result_dict["reqType" ]
    reqType_name =  [key for key, value in reqDict.items() if value == reqType_value]
    print('189:', '-'*48)
    print('188: reqType: ', reqType_value, reqType_name )
    for k,v in result_dict.items():
        print(f'135: {k:12}: {v}', )
    return result_dict


while True:
    user_input = input('\nEnter message to send > ')
    
    if user_input.lower() in ['exit', 'quit']:
        break

    tokens = user_input.split(' ')
    pack_data = pack_header(user_input)
    if pack_data:
        bytes_to_dict(pack_data)
    else: 
        print('144: error in pack dara')


    """
    try:
        # Create a new socket for this specific request
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
            client_socket.connect((HOST, PORT))
            
            print("Sending data to server...")
            # Sockets need BYTES, so we .encode() the raw string
            client_socket.sendall((user_input).encode('utf-8'))
            
            # Receive the response (up to 4096 bytes)
            response_bytes = client_socket.recv(4096)
            
        if not response_bytes:
            print("Server closed the connection.")
        else:
            # Decode the bytes back to a string for debugging (if needed)
            # decoded_response = response_bytes.decode('utf-8')

            # Open the file to save the incoming data
            with open(user_input, 'wb') as f:
                # Initially write the first chunk received
                f.write(response_bytes)
                
                # Continue receiving data until the server closes the connection
                while True:
                    chunk = client_socket.recv(CHUNK_SIZE)
                    if not chunk:
                        break
                    f.write(chunk)

            print(f"Data saved to {user_input}")         
            print("\n--- Data received back from server ---")
            

    except ConnectionRefusedError:
        print("Error: Could not connect to the server. Is it running?")
    except Exception as e:
        print(f"An error occurred: {e}")
    """
print("Client shut down.")