import tensorflow as tf
import numpy as np
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

# Read data from file
with open('D:/PHD Papers/scientific translation/GPT/First Code BiLSTM/EngArb.txt', 'r',encoding='utf8') as file:
    data = file.readlines()

# Split data into train, validate, and test
train_ratio = 0.6
val_ratio = 0.2
test_ratio = 0.2

num_examples = len(data)
num_train = int(num_examples * train_ratio)
num_val = int(num_examples * val_ratio)

train_data = data[:num_train]
val_data = data[num_train:num_train+num_val]
test_data = data[num_train+num_val:]

# Extract English and Arabic sentences from data
english_sentences_train = []
arabic_sentences_train = []

english_sentences_val = []
arabic_sentences_val = []

english_test = []
arabic_test = []

for line in train_data:
    english, arabic = line.strip().split('\t')
    english_sentences_train.append(english)
    arabic_sentences_train.append(arabic)

for line in val_data:
    english, arabic = line.strip().split('\t')
    english_sentences_val.append(english)
    arabic_sentences_val.append(arabic)

for line in test_data:
    english, arabic = line.strip().split('\t')
    english_test.append(english)
    arabic_test.append(arabic)

# Tokenize the sentences and convert them into numerical representations
tokenizer = Tokenizer(oov_token='<OOV>')
tokenizer.fit_on_texts(english_sentences_train + arabic_sentences_train)

english_train_sequences = tokenizer.texts_to_sequences(english_sentences_train)
arabic_train_sequences = tokenizer.texts_to_sequences(arabic_sentences_train)

english_val_sequences = tokenizer.texts_to_sequences(english_sentences_val)
arabic_val_sequences = tokenizer.texts_to_sequences(arabic_sentences_val)

english_test_sequences = tokenizer.texts_to_sequences(english_test)
arabic_test_sequences = tokenizer.texts_to_sequences(arabic_test)

# Pad or truncate the sentences to a fixed length
max_length = 10
english_train_padded = pad_sequences(english_train_sequences, maxlen=max_length, padding='post', truncating='post')
arabic_train_padded = pad_sequences(arabic_train_sequences, maxlen=max_length, padding='post', truncating='post')

english_val_padded = pad_sequences(english_val_sequences, maxlen=max_length, padding='post', truncating='post')
arabic_val_padded = pad_sequences(arabic_val_sequences, maxlen=max_length, padding='post', truncating='post')

english_test_padded = pad_sequences(english_test_sequences, maxlen=max_length, padding='post', truncating='post')
arabic_test_padded = pad_sequences(arabic_test_sequences, maxlen=max_length, padding='post', truncating='post')

# Define the BiLSTM encoder-decoder model
input_dim = len(tokenizer.word_index) + 1
output_dim = len(tokenizer.word_index) + 1
latent_dim = 256
embedding_dim = 100

encoder_inputs = tf.keras.layers.Input(shape=(max_length,))
encoder_embedding = tf.keras.layers.Embedding(input_dim, embedding_dim, input_length=max_length)(encoder_inputs)
encoder_gru = tf.keras.layers.Bidirectional(tf.keras.layers.GRU(latent_dim, return_state=True))
encoder_outputs, forward_h, forward_c, backward_h, backward_c = encoder_gru(encoder_embedding)
state_h = tf.keras.layers.Concatenate()([forward_h, backward_h])
state_c = tf.keras.layers.Concatenate()([forward_c, backward_c])

decoder_inputs = tf.keras.layers.Input(shape=(max_length,))
decoder_embedding = tf.keras.layers.Embedding(input_dim, embedding_dim, input_length=max_length)(decoder_inputs)
decoder_lstm = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(latent_dim * 2, return_sequences=True, return_state=True))
decoder_outputs, _ = decoder_lstm(decoder_embedding, initial_state=state_h)
decoder_dense = tf.keras.layers.Dense(output_dim, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)

model = tf.keras.models.Model([encoder_inputs, decoder_inputs], decoder_outputs)

# Compile and train the model
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit([english_train_padded, arabic_train_padded], np.expand_dims(arabic_train_padded, -1),
          epochs=30, batch_size=16, validation_data=([english_val_padded, arabic_val_padded], np.expand_dims(arabic_val_padded, -1)))

# Use test data to evaluate and example translation
predictions = model.predict([english_test_padded, arabic_test_padded])
predicted_sequences = np.argmax(predictions, axis=2)
predicted_sentences = tokenizer.sequences_to_texts(predicted_sequences)

for i in range(len(english_test)):
    print(f'English: {english_test[i]}')
    print(f'Arabic (Actual): {arabic_test[i]}')
    print(f'Arabic (Predicted): {predicted_sentences[i]}')
    print()